[
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!--\n\nThank you for proposing a pull request. This template will guide you through the essential steps necessary for a pull request.\nMake sure that:\n\n-->\n\n- [ ] You have read the [Spring Data contribution guidelines](https://github.com/spring-projects/spring-data-build/blob/master/CONTRIBUTING.adoc).\n- [ ] You use the code formatters provided [here](https://github.com/spring-projects/spring-data-build/tree/master/etc/ide) and have them applied to your changes. Don’t submit any formatting related changes.\n- [ ] You submit test cases (unit or integration tests) that back your changes.\n- [ ] You added yourself as author in the headers of the classes you touched. Amend the date range in the Apache license header if needed. For new types, add the license header (copy from another file and set the current year only).\n"
  },
  {
    "path": ".github/README.template.adoc",
    "content": "= Spring Data KeyValue image:https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A[\"Revved up by Develocity\", link=\"https://ge.spring.io/scans?search.rootProjectNames=Spring Data KeyValue\"]\n\nThe primary goal of the https://projects.spring.io/spring-data[Spring Data] project is to make it easier to build Spring-powered applications that use new data access technologies such as non-relational databases, map-reduce frameworks, and cloud based data services.\n\nThis module provides infrastructure components to build repository abstractions for stores dealing with Key/Value pairs and ships with a default `java.util.Map` based implementation.\n\n== Features\n\n* Infrastructure for building repositories on top of key/value implementations.\n* Dynamic SpEL query generation from query method names.\n* Possibility to integrate custom repository code.\n\n== Code of Conduct\n\nThis project is governed by the https://github.com/spring-projects/.github/blob/main/CODE_OF_CONDUCT.md[Spring Code of Conduct].\nBy participating, you are expected to uphold this code of conduct.\nPlease report unacceptable behavior to spring-code-of-conduct@pivotal.io.\n\n== Getting Started\n\nHere is a quick teaser of an application using Spring Data Repositories in Java:\n\n[source,java]\n----\npublic interface PersonRepository extends CrudRepository<Person, Long> {\n\n    List<Person> findByLastname(String lastname);\n\n    List<Person> findByFirstnameLike(String firstname);\n}\n\n@Service\npublic class MyService {\n\n    private final PersonRepository repository;\n\n    public MyService(PersonRepository repository) {\n        this.repository = repository;\n    }\n\n    public void doWork() {\n\n        repository.deleteAll();\n\n        Person person = new Person();\n        person.setFirstname(\"Oliver\");\n        person.setLastname(\"Gierke\");\n        repository.save(person);\n\n        List<Person> lastNameResults = repository.findByLastname(\"Gierke\");\n        List<Person> firstNameResults = repository.findByFirstnameLike(\"Oli*\");\n    }\n}\n\n@KeySpace(\"person\")\nclass Person {\n\n    @Id String uuid;\n    String firstname;\n    String lastname;\n\n    // getters and setters omitted for brevity\n}\n\n@Configuration\n@EnableMapRepositories(\"com.acme.repositories\")\nclass AppConfig { … }\n----\n\n=== Maven configuration\n\nAdd the Maven dependency:\n\n[source,xml]\n----\n<dependency>\n    <groupId>org.springframework.data</groupId>\n    <artifactId>spring-data-keyvalue</artifactId>\n    <version>${version}.RELEASE</version>\n</dependency>\n----\n\nIf you'd rather like the latest snapshots of the upcoming major version, use our Maven snapshot repository and declare the appropriate dependency version.\n\n[source,xml]\n----\n<dependency>\n    <groupId>org.springframework.data</groupId>\n    <artifactId>spring-data-keyvalue</artifactId>\n    <version>${version}-SNAPSHOT</version>\n</dependency>\n\n<repository>\n    <id>spring-snapshot</id>\n    <name>Spring Snapshot Repository</name>\n    <url>https://repo.spring.io/snapshot</url>\n</repository>\n----\n\n== Getting Help\n\nHaving trouble with Spring Data?\nWe’d love to help!\n\n* Check the\nhttps://docs.spring.io/spring-data/keyvalue/reference/[reference documentation], and https://docs.spring.io/spring-data/keyvalue/docs/current/api/[Javadocs].\n* Learn the Spring basics – Spring Data builds on Spring Framework, check the https://spring.io[spring.io] web-site for a wealth of reference documentation.\nIf you are just starting out with Spring, try one of the https://spring.io/guides[guides].\n* If you are upgrading, check out the https://github.com/spring-projects/spring-data-commons/wiki#release-notes[release notes] for \"`new and noteworthy`\" features.\n* Ask a question - we monitor https://stackoverflow.com[stackoverflow.com] for questions tagged with https://stackoverflow.com/tags/spring-data[`spring-data-keyvalue`].\n* Report bugs with Spring Data KeyValue via https://github.com/spring-projects/spring-data-keyvalue/issues[Github].\n\n== Reporting Issues\n\nSpring Data uses Github as issue tracking system to record bugs and feature requests.\nIf you want to raise an issue, please follow the recommendations below:\n\n* Before you log a bug, please search the https://github.com/spring-projects/spring-data-keyvalue/issues[issue tracker] to see if someone has already reported the problem.\n* If the issue does not already exist, https://github.com/spring-projects/spring-data-keyvalue/issues/new[create a new issue].\n* Please provide as much information as possible with the issue report, we like to know the version of Spring Data that you are using, the JVM version, Stacktrace, etc.\n* If you need to paste code, or include a stack trace use https://guides.github.com/features/mastering-markdown/[Markdown] code fences +++```+++.\n* If possible try to create a test-case or project that replicates the issue.\nAttach a link to your code or a compressed file containing your code.\n\n== Building from Source\n\nYou don’t need to build from source to use Spring Data (binaries in https://repo.spring.io[repo.spring.io]), but if you want to try out the latest and greatest, Spring Data can be easily built with the https://github.com/takari/maven-wrapper[maven wrapper].\nYou also need JDK 17.\n\n[source,bash]\n----\n $ ./mvnw clean install\n----\n\nIf you want to build with the regular `mvn` command, you will need https://maven.apache.org/run-maven/index.html[Maven v3.5.0 or above].\n\n_Also see link:CONTRIBUTING.adoc[CONTRIBUTING.adoc] if you wish to submit pull requests, and in particular please sign the https://cla.pivotal.io/sign/spring[Contributor’s Agreement] before your first change, is trivial._\n\n=== Building reference documentation\n\nBuilding the documentation builds also the project without running tests.\n\n[source,bash]\n----\n $ ./mvnw clean install -Pantora\n----\n\nThe generated documentation is available from `target/antora/site/index.html`.\n\n== Examples\n\n* https://github.com/spring-projects/spring-data-examples/[Spring Data Examples] contains example projects that explain specific features in more detail.\n\n== License\n\nSpring Data KeyValue is Open Source software released under the https://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 license].\n"
  },
  {
    "path": ".github/dco.yml",
    "content": "require:\n  members: false\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI Build\n\non:\n  workflow_dispatch:\n  push:\n    branches: [ main, 'issue/**' ]\n\npermissions: read-all\n\njobs:\n  build-java:\n    strategy:\n      matrix:\n        java-version: [ base, main ]\n    name: Build project\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - name: Setup Java and Maven\n        uses: spring-projects/spring-data-build/actions/setup-maven@main\n        with:\n          java-version: ${{ matrix.java-version }}\n          develocity-access-key: '${{ secrets.DEVELOCITY_ACCESS_KEY }}'\n      - name: Build\n        uses: spring-projects/spring-data-build/actions/maven-build@main\n"
  },
  {
    "path": ".github/workflows/codeql.yml",
    "content": "# GitHub Actions for CodeQL Scanning\n\nname: \"CodeQL Advanced\"\n\non:\n  push:\n  pull_request:\n  workflow_dispatch:\n  schedule:\n    # https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#schedule\n    - cron: '0 5 * * *'\n\npermissions: read-all\n\njobs:\n  codeql-analysis-call:\n    permissions:\n      actions: read\n      contents: read\n      security-events: write\n    uses: spring-io/github-actions/.github/workflows/codeql-analysis.yml@1\n"
  },
  {
    "path": ".github/workflows/project.yml",
    "content": "# GitHub Actions to automate GitHub issues for Spring Data Project Management\n\nname: Spring Data GitHub Issues\n\non:\n  issues:\n    types: [opened, edited, reopened]\n  issue_comment:\n    types: [created]\n  pull_request_target:\n    types: [opened, edited, reopened]\n\npermissions:\n  contents: read\n  issues: write\n  pull-requests: write\n\njobs:\n  Inbox:\n    runs-on: ubuntu-latest\n    if: github.repository_owner == 'spring-projects' && (github.event.action == 'opened' || github.event.action == 'reopened') && github.event.pull_request == null && !contains(join(github.event.issue.labels.*.name, ', '), 'dependency-upgrade') && !contains(github.event.issue.title, 'Release ')\n    steps:\n      - name: Create or Update Issue Card\n        uses: actions/add-to-project@v1.0.2\n        with:\n          project-url: https://github.com/orgs/spring-projects/projects/25\n          github-token: ${{ secrets.GH_ISSUES_TOKEN_SPRING_DATA }}\n  Pull-Request:\n    runs-on: ubuntu-latest\n    if: github.repository_owner == 'spring-projects' && (github.event.action == 'opened' || github.event.action == 'reopened') && github.event.pull_request != null\n    steps:\n      - name: Create or Update Pull Request Card\n        uses: actions/add-to-project@v1.0.2\n        with:\n          project-url: https://github.com/orgs/spring-projects/projects/25\n          github-token: ${{ secrets.GH_ISSUES_TOKEN_SPRING_DATA }}\n  Feedback-Provided:\n    runs-on: ubuntu-latest\n    if: github.repository_owner == 'spring-projects' && github.event_name == 'issue_comment' && github.event.action == 'created' && github.actor != 'spring-projects-issues' && github.event.pull_request == null && github.event.issue.state == 'open' && contains(toJSON(github.event.issue.labels), 'waiting-for-feedback')\n    steps:\n      - name: Update Project Card\n        uses: actions/add-to-project@v1.0.2\n        with:\n          project-url: https://github.com/orgs/spring-projects/projects/25\n          github-token: ${{ secrets.GH_ISSUES_TOKEN_SPRING_DATA }}\n"
  },
  {
    "path": ".github/workflows/snapshots.yml",
    "content": "name: Snapshots\n\non:\n  workflow_dispatch:\n  push:\n    branches: [ main, 'issue/**' ]\n\npermissions: read-all\n\njobs:\n  build-snapshots:\n    name: Build and deploy snapshots\n    if: ${{ github.repository_owner == 'spring-projects' }}\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - name: Setup Java and Maven\n        uses: spring-projects/spring-data-build/actions/setup-maven@main\n        with:\n          develocity-access-key: '${{ secrets.DEVELOCITY_ACCESS_KEY }}'\n      - name: Deploy to Artifactory\n        uses: spring-projects/spring-data-build/actions/maven-artifactory-deploy@main\n        with:\n          build-name: 'spring-data-keyvalue'\n          username: '${{ secrets.ARTIFACTORY_USERNAME }}'\n          password: '${{ secrets.ARTIFACTORY_PASSWORD }}'\n"
  },
  {
    "path": ".gitignore",
    "content": "target/\n.settings/\n.project\n.classpath\n.springBeans\n.DS_Store\n*.iml\n*.ipr\n*.iws\n/.idea/\nnode_modules\nnode\npackage-lock.json\nbuild/\n.mvn/.develocity\n"
  },
  {
    "path": ".mvn/extensions.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<extensions>\n\t<extension>\n\t\t<groupId>io.spring.develocity.conventions</groupId>\n\t\t<artifactId>develocity-conventions-maven-extension</artifactId>\n\t\t<version>0.0.25</version>\n\t</extension>\n</extensions>\n"
  },
  {
    "path": ".mvn/jvm.config",
    "content": "--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED\n--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED\n--add-exports jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED\n--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED\n--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED\n--add-exports jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED\n--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED\n--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED\n--add-opens jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED\n--add-opens jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED\n--add-opens=java.base/java.util=ALL-UNNAMED\n--add-opens=java.base/java.lang.reflect=ALL-UNNAMED\n--add-opens=java.base/java.text=ALL-UNNAMED\n--add-opens=java.desktop/java.awt.font=ALL-UNNAMED\n"
  },
  {
    "path": ".mvn/wrapper/maven-wrapper.properties",
    "content": "#Thu Jul 17 13:59:50 CEST 2025\ndistributionUrl=https\\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip\n"
  },
  {
    "path": "CI.adoc",
    "content": "= Continuous Integration\n\n== Running CI tasks locally\n\nYou can run CI jobs locally using Docker and act[https://nektosact.com/].\n"
  },
  {
    "path": "CONTRIBUTING.adoc",
    "content": "= Spring Data contribution guidelines\n\nYou find the contribution guidelines for Spring Data projects https://github.com/spring-projects/spring-data-build/blob/main/CONTRIBUTING.adoc[here].\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        https://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   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright {yyyy} {name of copyright owner}\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       https://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.\n"
  },
  {
    "path": "README.adoc",
    "content": "= Spring Data KeyValue image:https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A[\"Revved up by Develocity\", link=\"https://ge.spring.io/scans?search.rootProjectNames=Spring Data KeyValue\"]\n\nThe primary goal of the https://projects.spring.io/spring-data[Spring Data] project is to make it easier to build Spring-powered applications that use new data access technologies such as non-relational databases, map-reduce frameworks, and cloud based data services.\n\nThis module provides infrastructure components to build repository abstractions for stores dealing with Key/Value pairs and ships with a default `java.util.Map` based implementation.\n\n== Features\n\n* Infrastructure for building repositories on top of key/value implementations.\n* Dynamic SpEL query generation from query method names.\n* Possibility to integrate custom repository code.\n\ninclude::https://raw.githubusercontent.com/spring-projects/spring-data-build/refs/heads/main/etc/readme/code-of-conduct.adoc[]\n\n== Getting Started\n\nHere is a quick teaser of an application using Spring Data Repositories in Java:\n\n[source,java]\n----\npublic interface PersonRepository extends CrudRepository<Person, Long> {\n\n    List<Person> findByLastname(String lastname);\n\n    List<Person> findByFirstnameLike(String firstname);\n}\n\n@Service\npublic class MyService {\n\n    private final PersonRepository repository;\n\n    public MyService(PersonRepository repository) {\n        this.repository = repository;\n    }\n\n    public void doWork() {\n\n        repository.deleteAll();\n\n        Person person = new Person();\n        person.setFirstname(\"Oliver\");\n        person.setLastname(\"Gierke\");\n        repository.save(person);\n\n        List<Person> lastNameResults = repository.findByLastname(\"Gierke\");\n        List<Person> firstNameResults = repository.findByFirstnameLike(\"Oli*\");\n    }\n}\n\n@KeySpace(\"person\")\nclass Person {\n\n    @Id String uuid;\n    String firstname;\n    String lastname;\n\n    // getters and setters omitted for brevity\n}\n\n@Configuration\n@EnableMapRepositories(\"com.acme.repositories\")\nclass AppConfig { … }\n----\n\ninclude::https://raw.githubusercontent.com/spring-projects/spring-data-build/refs/heads/main/etc/readme/dependencies.adoc[]\n\ninclude::https://raw.githubusercontent.com/spring-projects/spring-data-build/refs/heads/main/etc/readme/getting-help.adoc[]\n\ninclude::https://raw.githubusercontent.com/spring-projects/spring-data-build/refs/heads/main/etc/readme/license.adoc[]\n"
  },
  {
    "path": "SECURITY.adoc",
    "content": "= Security Policy\n\n== Reporting a Vulnerability\n\nPlease, https://github.com/spring-projects/security-advisories/security/advisories/new[open a draft security advisory] if you need to disclose and discuss a security issue in private with the Spring Data team.\nNote that we only accept reports against https://spring.io/projects/spring-data#support[supported versions].\n\nFor more details, check out our https://spring.io/security-policy[security policy].\n\n== JAR signing\n\nSpring Data JARs released on Maven Central are signed.\nYou'll find more information about the key here: https://spring.io/GPG-KEY-spring.txt\n\nVersions released prior to 2023 may be signed with a different key.\n"
  },
  {
    "path": "mvnw",
    "content": "#!/bin/sh\n# ----------------------------------------------------------------------------\n# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#    https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\n# ----------------------------------------------------------------------------\n\n# ----------------------------------------------------------------------------\n# Maven2 Start Up Batch script\n#\n# Required ENV vars:\n# ------------------\n#   JAVA_HOME - location of a JDK home dir\n#\n# Optional ENV vars\n# -----------------\n#   M2_HOME - location of maven2's installed home dir\n#   MAVEN_OPTS - parameters passed to the Java VM when running Maven\n#     e.g. to debug Maven itself, use\n#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000\n#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files\n# ----------------------------------------------------------------------------\n\nif [ -z \"$MAVEN_SKIP_RC\" ] ; then\n\n  if [ -f /etc/mavenrc ] ; then\n    . /etc/mavenrc\n  fi\n\n  if [ -f \"$HOME/.mavenrc\" ] ; then\n    . \"$HOME/.mavenrc\"\n  fi\n\nfi\n\n# OS specific support.  $var _must_ be set to either true or false.\ncygwin=false;\ndarwin=false;\nmingw=false\ncase \"`uname`\" in\n  CYGWIN*) cygwin=true ;;\n  MINGW*) mingw=true;;\n  Darwin*) darwin=true\n    # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home\n    # See https://developer.apple.com/library/mac/qa/qa1170/_index.html\n    if [ -z \"$JAVA_HOME\" ]; then\n      if [ -x \"/usr/libexec/java_home\" ]; then\n        export JAVA_HOME=\"`/usr/libexec/java_home`\"\n      else\n        export JAVA_HOME=\"/Library/Java/Home\"\n      fi\n    fi\n    ;;\nesac\n\nif [ -z \"$JAVA_HOME\" ] ; then\n  if [ -r /etc/gentoo-release ] ; then\n    JAVA_HOME=`java-config --jre-home`\n  fi\nfi\n\nif [ -z \"$M2_HOME\" ] ; then\n  ## resolve links - $0 may be a link to maven's home\n  PRG=\"$0\"\n\n  # need this for relative symlinks\n  while [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n      PRG=\"$link\"\n    else\n      PRG=\"`dirname \"$PRG\"`/$link\"\n    fi\n  done\n\n  saveddir=`pwd`\n\n  M2_HOME=`dirname \"$PRG\"`/..\n\n  # make it fully qualified\n  M2_HOME=`cd \"$M2_HOME\" && pwd`\n\n  cd \"$saveddir\"\n  # echo Using m2 at $M2_HOME\nfi\n\n# For Cygwin, ensure paths are in UNIX format before anything is touched\nif $cygwin ; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=`cygpath --unix \"$M2_HOME\"`\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=`cygpath --unix \"$JAVA_HOME\"`\n  [ -n \"$CLASSPATH\" ] &&\n    CLASSPATH=`cygpath --path --unix \"$CLASSPATH\"`\nfi\n\n# For Mingw, ensure paths are in UNIX format before anything is touched\nif $mingw ; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=\"`(cd \"$M2_HOME\"; pwd)`\"\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=\"`(cd \"$JAVA_HOME\"; pwd)`\"\n  # TODO classpath?\nfi\n\nif [ -z \"$JAVA_HOME\" ]; then\n  javaExecutable=\"`which javac`\"\n  if [ -n \"$javaExecutable\" ] && ! [ \"`expr \\\"$javaExecutable\\\" : '\\([^ ]*\\)'`\" = \"no\" ]; then\n    # readlink(1) is not available as standard on Solaris 10.\n    readLink=`which readlink`\n    if [ ! `expr \"$readLink\" : '\\([^ ]*\\)'` = \"no\" ]; then\n      if $darwin ; then\n        javaHome=\"`dirname \\\"$javaExecutable\\\"`\"\n        javaExecutable=\"`cd \\\"$javaHome\\\" && pwd -P`/javac\"\n      else\n        javaExecutable=\"`readlink -f \\\"$javaExecutable\\\"`\"\n      fi\n      javaHome=\"`dirname \\\"$javaExecutable\\\"`\"\n      javaHome=`expr \"$javaHome\" : '\\(.*\\)/bin'`\n      JAVA_HOME=\"$javaHome\"\n      export JAVA_HOME\n    fi\n  fi\nfi\n\nif [ -z \"$JAVACMD\" ] ; then\n  if [ -n \"$JAVA_HOME\"  ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n      # IBM's JDK on AIX uses strange locations for the executables\n      JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n      JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n  else\n    JAVACMD=\"`which java`\"\n  fi\nfi\n\nif [ ! -x \"$JAVACMD\" ] ; then\n  echo \"Error: JAVA_HOME is not defined correctly.\" >&2\n  echo \"  We cannot execute $JAVACMD\" >&2\n  exit 1\nfi\n\nif [ -z \"$JAVA_HOME\" ] ; then\n  echo \"Warning: JAVA_HOME environment variable is not set.\"\nfi\n\nCLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher\n\n# traverses directory structure from process work directory to filesystem root\n# first directory with .mvn subdirectory is considered project base directory\nfind_maven_basedir() {\n\n  if [ -z \"$1\" ]\n  then\n    echo \"Path not specified to find_maven_basedir\"\n    return 1\n  fi\n\n  basedir=\"$1\"\n  wdir=\"$1\"\n  while [ \"$wdir\" != '/' ] ; do\n    if [ -d \"$wdir\"/.mvn ] ; then\n      basedir=$wdir\n      break\n    fi\n    # workaround for JBEAP-8937 (on Solaris 10/Sparc)\n    if [ -d \"${wdir}\" ]; then\n      wdir=`cd \"$wdir/..\"; pwd`\n    fi\n    # end of workaround\n  done\n  echo \"${basedir}\"\n}\n\n# concatenates all lines of a file\nconcat_lines() {\n  if [ -f \"$1\" ]; then\n    echo \"$(tr -s '\\n' ' ' < \"$1\")\"\n  fi\n}\n\nBASE_DIR=`find_maven_basedir \"$(pwd)\"`\nif [ -z \"$BASE_DIR\" ]; then\n  exit 1;\nfi\n\n##########################################################################################\n# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central\n# This allows using the maven wrapper in projects that prohibit checking in binary data.\n##########################################################################################\nif [ -r \"$BASE_DIR/.mvn/wrapper/maven-wrapper.jar\" ]; then\n    if [ \"$MVNW_VERBOSE\" = true ]; then\n      echo \"Found .mvn/wrapper/maven-wrapper.jar\"\n    fi\nelse\n    if [ \"$MVNW_VERBOSE\" = true ]; then\n      echo \"Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ...\"\n    fi\n    jarUrl=\"https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar\"\n    while IFS=\"=\" read key value; do\n      case \"$key\" in (wrapperUrl) jarUrl=\"$value\"; break ;;\n      esac\n    done < \"$BASE_DIR/.mvn/wrapper/maven-wrapper.properties\"\n    if [ \"$MVNW_VERBOSE\" = true ]; then\n      echo \"Downloading from: $jarUrl\"\n    fi\n    wrapperJarPath=\"$BASE_DIR/.mvn/wrapper/maven-wrapper.jar\"\n\n    if command -v wget > /dev/null; then\n        if [ \"$MVNW_VERBOSE\" = true ]; then\n          echo \"Found wget ... using wget\"\n        fi\n        wget \"$jarUrl\" -O \"$wrapperJarPath\"\n    elif command -v curl > /dev/null; then\n        if [ \"$MVNW_VERBOSE\" = true ]; then\n          echo \"Found curl ... using curl\"\n        fi\n        curl -o \"$wrapperJarPath\" \"$jarUrl\"\n    else\n        if [ \"$MVNW_VERBOSE\" = true ]; then\n          echo \"Falling back to using Java to download\"\n        fi\n        javaClass=\"$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java\"\n        if [ -e \"$javaClass\" ]; then\n            if [ ! -e \"$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class\" ]; then\n                if [ \"$MVNW_VERBOSE\" = true ]; then\n                  echo \" - Compiling MavenWrapperDownloader.java ...\"\n                fi\n                # Compiling the Java class\n                (\"$JAVA_HOME/bin/javac\" \"$javaClass\")\n            fi\n            if [ -e \"$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class\" ]; then\n                # Running the downloader\n                if [ \"$MVNW_VERBOSE\" = true ]; then\n                  echo \" - Running MavenWrapperDownloader.java ...\"\n                fi\n                (\"$JAVA_HOME/bin/java\" -cp .mvn/wrapper MavenWrapperDownloader \"$MAVEN_PROJECTBASEDIR\")\n            fi\n        fi\n    fi\nfi\n##########################################################################################\n# End of extension\n##########################################################################################\n\nexport MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-\"$BASE_DIR\"}\nif [ \"$MVNW_VERBOSE\" = true ]; then\n  echo $MAVEN_PROJECTBASEDIR\nfi\nMAVEN_OPTS=\"$(concat_lines \"$MAVEN_PROJECTBASEDIR/.mvn/jvm.config\") $MAVEN_OPTS\"\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin; then\n  [ -n \"$M2_HOME\" ] &&\n    M2_HOME=`cygpath --path --windows \"$M2_HOME\"`\n  [ -n \"$JAVA_HOME\" ] &&\n    JAVA_HOME=`cygpath --path --windows \"$JAVA_HOME\"`\n  [ -n \"$CLASSPATH\" ] &&\n    CLASSPATH=`cygpath --path --windows \"$CLASSPATH\"`\n  [ -n \"$MAVEN_PROJECTBASEDIR\" ] &&\n    MAVEN_PROJECTBASEDIR=`cygpath --path --windows \"$MAVEN_PROJECTBASEDIR\"`\nfi\n\nWRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain\n\nexec \"$JAVACMD\" \\\n  $MAVEN_OPTS \\\n  -classpath \"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar\" \\\n  \"-Dmaven.home=${M2_HOME}\" \"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}\" \\\n  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG \"$@\"\n"
  },
  {
    "path": "mvnw.cmd",
    "content": "@REM ----------------------------------------------------------------------------\n@REM Licensed to the Apache Software Foundation (ASF) under one\n@REM or more contributor license agreements.  See the NOTICE file\n@REM distributed with this work for additional information\n@REM regarding copyright ownership.  The ASF licenses this file\n@REM to you under the Apache License, Version 2.0 (the\n@REM \"License\"); you may not use this file except in compliance\n@REM with the License.  You may obtain a copy of the License at\n@REM\n@REM    https://www.apache.org/licenses/LICENSE-2.0\n@REM\n@REM Unless required by applicable law or agreed to in writing,\n@REM software distributed under the License is distributed on an\n@REM \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n@REM KIND, either express or implied.  See the License for the\n@REM specific language governing permissions and limitations\n@REM under the License.\n@REM ----------------------------------------------------------------------------\n\n@REM ----------------------------------------------------------------------------\n@REM Maven2 Start Up Batch script\n@REM\n@REM Required ENV vars:\n@REM JAVA_HOME - location of a JDK home dir\n@REM\n@REM Optional ENV vars\n@REM M2_HOME - location of maven2's installed home dir\n@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands\n@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending\n@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven\n@REM     e.g. to debug Maven itself, use\n@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000\n@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files\n@REM ----------------------------------------------------------------------------\n\n@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'\n@echo off\n@REM set title of command window\ntitle %0\n@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'\n@if \"%MAVEN_BATCH_ECHO%\" == \"on\"  echo %MAVEN_BATCH_ECHO%\n\n@REM set %HOME% to equivalent of $HOME\nif \"%HOME%\" == \"\" (set \"HOME=%HOMEDRIVE%%HOMEPATH%\")\n\n@REM Execute a user defined script before this one\nif not \"%MAVEN_SKIP_RC%\" == \"\" goto skipRcPre\n@REM check for pre script, once with legacy .bat ending and once with .cmd ending\nif exist \"%HOME%\\mavenrc_pre.bat\" call \"%HOME%\\mavenrc_pre.bat\"\nif exist \"%HOME%\\mavenrc_pre.cmd\" call \"%HOME%\\mavenrc_pre.cmd\"\n:skipRcPre\n\n@setlocal\n\nset ERROR_CODE=0\n\n@REM To isolate internal variables from possible post scripts, we use another setlocal\n@setlocal\n\n@REM ==== START VALIDATION ====\nif not \"%JAVA_HOME%\" == \"\" goto OkJHome\n\necho.\necho Error: JAVA_HOME not found in your environment. >&2\necho Please set the JAVA_HOME variable in your environment to match the >&2\necho location of your Java installation. >&2\necho.\ngoto error\n\n:OkJHome\nif exist \"%JAVA_HOME%\\bin\\java.exe\" goto init\n\necho.\necho Error: JAVA_HOME is set to an invalid directory. >&2\necho JAVA_HOME = \"%JAVA_HOME%\" >&2\necho Please set the JAVA_HOME variable in your environment to match the >&2\necho location of your Java installation. >&2\necho.\ngoto error\n\n@REM ==== END VALIDATION ====\n\n:init\n\n@REM Find the project base dir, i.e. the directory that contains the folder \".mvn\".\n@REM Fallback to current working directory if not found.\n\nset MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%\nIF NOT \"%MAVEN_PROJECTBASEDIR%\"==\"\" goto endDetectBaseDir\n\nset EXEC_DIR=%CD%\nset WDIR=%EXEC_DIR%\n:findBaseDir\nIF EXIST \"%WDIR%\"\\.mvn goto baseDirFound\ncd ..\nIF \"%WDIR%\"==\"%CD%\" goto baseDirNotFound\nset WDIR=%CD%\ngoto findBaseDir\n\n:baseDirFound\nset MAVEN_PROJECTBASEDIR=%WDIR%\ncd \"%EXEC_DIR%\"\ngoto endDetectBaseDir\n\n:baseDirNotFound\nset MAVEN_PROJECTBASEDIR=%EXEC_DIR%\ncd \"%EXEC_DIR%\"\n\n:endDetectBaseDir\n\nIF NOT EXIST \"%MAVEN_PROJECTBASEDIR%\\.mvn\\jvm.config\" goto endReadAdditionalConfig\n\n@setlocal EnableExtensions EnableDelayedExpansion\nfor /F \"usebackq delims=\" %%a in (\"%MAVEN_PROJECTBASEDIR%\\.mvn\\jvm.config\") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a\n@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%\n\n:endReadAdditionalConfig\n\nSET MAVEN_JAVA_EXE=\"%JAVA_HOME%\\bin\\java.exe\"\nset WRAPPER_JAR=\"%MAVEN_PROJECTBASEDIR%\\.mvn\\wrapper\\maven-wrapper.jar\"\nset WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain\n\nset DOWNLOAD_URL=\"https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar\"\nFOR /F \"tokens=1,2 delims==\" %%A IN (%MAVEN_PROJECTBASEDIR%\\.mvn\\wrapper\\maven-wrapper.properties) DO (\n\tIF \"%%A\"==\"wrapperUrl\" SET DOWNLOAD_URL=%%B \n)\n\n@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central\n@REM This allows using the maven wrapper in projects that prohibit checking in binary data.\nif exist %WRAPPER_JAR% (\n    echo Found %WRAPPER_JAR%\n) else (\n    echo Couldn't find %WRAPPER_JAR%, downloading it ...\n\techo Downloading from: %DOWNLOAD_URL%\n    powershell -Command \"(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')\"\n    echo Finished downloading %WRAPPER_JAR%\n)\n@REM End of extension\n\n%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% \"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%\" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*\nif ERRORLEVEL 1 goto error\ngoto end\n\n:error\nset ERROR_CODE=1\n\n:end\n@endlocal & set ERROR_CODE=%ERROR_CODE%\n\nif not \"%MAVEN_SKIP_RC%\" == \"\" goto skipRcPost\n@REM check for post script, once with legacy .bat ending and once with .cmd ending\nif exist \"%HOME%\\mavenrc_post.bat\" call \"%HOME%\\mavenrc_post.bat\"\nif exist \"%HOME%\\mavenrc_post.cmd\" call \"%HOME%\\mavenrc_post.cmd\"\n:skipRcPost\n\n@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'\nif \"%MAVEN_BATCH_PAUSE%\" == \"on\" pause\n\nif \"%MAVEN_TERMINATE_CMD%\" == \"on\" exit %ERROR_CODE%\n\nexit /B %ERROR_CODE%\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"dependencies\": {\n    \"antora\": \"3.2.0-alpha.6\",\n    \"@antora/atlas-extension\": \"1.0.0-alpha.2\",\n    \"@antora/collector-extension\": \"1.0.0-alpha.7\",\n    \"@asciidoctor/tabs\": \"1.0.0-beta.6\",\n    \"@springio/antora-extensions\": \"1.13.0\",\n    \"@springio/asciidoctor-extensions\": \"1.0.0-alpha.11\"\n  }\n}\n"
  },
  {
    "path": "pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>org.springframework.data</groupId>\n\t<artifactId>spring-data-keyvalue</artifactId>\n\t<version>4.1.0-SNAPSHOT</version>\n\n\t<name>Spring Data KeyValue</name>\n\n\t<parent>\n\t\t<groupId>org.springframework.data.build</groupId>\n\t\t<artifactId>spring-data-parent</artifactId>\n\t\t<version>4.1.0-SNAPSHOT</version>\n\t\t<relativePath/>\n\t</parent>\n\n\t<properties>\n\t\t<springdata.commons>4.1.0-SNAPSHOT</springdata.commons>\n\t\t<mapdb>3.1.0</mapdb>\n\t\t<java-module-name>spring.data.keyvalue</java-module-name>\n\t</properties>\n\n\t<dependencies>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.data</groupId>\n\t\t\t<artifactId>spring-data-commons</artifactId>\n\t\t\t<version>${springdata.commons}</version>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-context</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-tx</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>com.querydsl</groupId>\n\t\t\t<artifactId>querydsl-collections</artifactId>\n\t\t\t<version>${querydsl}</version>\n\t\t\t<optional>true</optional>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.mapdb</groupId>\n\t\t\t<artifactId>mapdb</artifactId>\n\t\t\t<version>${mapdb}</version>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\n\t\t\t<plugin>\n\t\t\t\t<groupId>com.mysema.maven</groupId>\n\t\t\t\t<artifactId>apt-maven-plugin</artifactId>\n\t\t\t\t<version>${apt}</version>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<phase>generate-test-sources</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>test-process</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t\t<configuration>\n\t\t\t\t\t\t\t<outputDirectory>target/generated-test-sources\n\t\t\t\t\t\t\t</outputDirectory>\n\t\t\t\t\t\t\t<processor>com.querydsl.apt.QuerydslAnnotationProcessor</processor>\n\t\t\t\t\t\t</configuration>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t</plugin>\n\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-assembly-plugin</artifactId>\n\t\t\t</plugin>\n\n\t\t</plugins>\n\t</build>\n\n\t<profiles>\n\t\t<profile>\n\t\t\t<!-- placeholder for no profile -->\n\t\t\t<id>none</id>\n\t\t</profile>\n\t\t<profile>\n\t\t\t<id>antora-process-resources</id>\n\t\t\t<build>\n\t\t\t\t<resources>\n\t\t\t\t\t<resource>\n\t\t\t\t\t\t<directory>src/main/antora/resources/antora-resources</directory>\n\t\t\t\t\t\t<filtering>true</filtering>\n\t\t\t\t\t</resource>\n\t\t\t\t</resources>\n\t\t\t</build>\n\t\t</profile>\n\t\t<profile>\n\t\t\t<id>antora</id>\n\t\t\t<build>\n\t\t\t\t<plugins>\n\t\t\t\t\t<plugin>\n\t\t\t\t\t\t<groupId>org.antora</groupId>\n\t\t\t\t\t\t<artifactId>antora-maven-plugin</artifactId>\n\t\t\t\t\t</plugin>\n\t\t\t\t</plugins>\n\t\t\t</build>\n\t\t</profile>\n\t</profiles>\n\n\t<repositories>\n\t\t<repository>\n\t\t\t<id>spring-snapshot</id>\n\t\t\t<url>https://repo.spring.io/snapshot</url>\n\t\t\t<snapshots>\n\t\t\t\t<enabled>true</enabled>\n\t\t\t</snapshots>\n\t\t\t<releases>\n\t\t\t\t<enabled>false</enabled>\n\t\t\t</releases>\n\t\t</repository>\n\t\t<repository>\n\t\t\t<id>spring-milestone</id>\n\t\t\t<url>https://repo.spring.io/milestone</url>\n\t\t</repository>\n\t</repositories>\n\n</project>\n"
  },
  {
    "path": "settings.xml",
    "content": "<settings xmlns=\"http://maven.apache.org/SETTINGS/1.0.0\"\n          xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n          xsi:schemaLocation=\"http://maven.apache.org/SETTINGS/1.0.0\n                      https://maven.apache.org/xsd/settings-1.0.0.xsd\">\n\n\t<servers>\n\t\t<server>\n\t\t\t<id>spring-plugins-release</id>\n\t\t\t<username>${env.ARTIFACTORY_USR}</username>\n\t\t\t<password>${env.ARTIFACTORY_PSW}</password>\n\t\t</server>\n\t\t<server>\n\t\t\t<id>spring-libs-snapshot</id>\n\t\t\t<username>${env.ARTIFACTORY_USR}</username>\n\t\t\t<password>${env.ARTIFACTORY_PSW}</password>\n\t\t</server>\n\t\t<server>\n\t\t\t<id>spring-libs-milestone</id>\n\t\t\t<username>${env.ARTIFACTORY_USR}</username>\n\t\t\t<password>${env.ARTIFACTORY_PSW}</password>\n\t\t</server>\n\t\t<server>\n\t\t\t<id>spring-libs-release</id>\n\t\t\t<username>${env.ARTIFACTORY_USR}</username>\n\t\t\t<password>${env.ARTIFACTORY_PSW}</password>\n\t\t</server>\n\t</servers>\n\n</settings>"
  },
  {
    "path": "src/main/antora/antora-playbook.yml",
    "content": "# PACKAGES antora@3.2.0-alpha.2 @antora/atlas-extension:1.0.0-alpha.1 @antora/collector-extension@1.0.0-alpha.3 @springio/antora-extensions@1.1.0-alpha.2 @asciidoctor/tabs@1.0.0-alpha.12 @opendevise/antora-release-line-extension@1.0.0-alpha.2\n#\n# The purpose of this Antora playbook is to build the docs in the current branch.\nantora:\n  extensions:\n    - require: '@springio/antora-extensions'\n      root_component_name: 'data-keyvalue'\nsite:\n  title: Spring Data KeyValue\n  url: https://docs.spring.io/spring-data/keyvalue/reference/\ncontent:\n  sources:\n    - url: ./../../..\n      branches: HEAD\n      start_path: src/main/antora\n      worktrees: true\n    - url: https://github.com/spring-projects/spring-data-commons\n      # Refname matching:\n      # https://docs.antora.org/antora/latest/playbook/content-refname-matching/\n      branches: [ main ]\n      start_path: src/main/antora\nasciidoc:\n  attributes:\n    hide-uri-scheme: '@'\n    tabs-sync-option: '@'\n  extensions:\n    - '@asciidoctor/tabs'\n    - '@springio/asciidoctor-extensions'\n    - '@springio/asciidoctor-extensions/javadoc-extension'\n  sourcemap: true\nurls:\n  latest_version_segment: ''\nruntime:\n  log:\n    failure_level: warn\n    format: pretty\nui:\n  bundle:\n    url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.16/ui-bundle.zip\n    snapshot: true\n"
  },
  {
    "path": "src/main/antora/antora.yml",
    "content": "name: data-keyvalue\nversion: true\ntitle: Spring Data KeyValue\nnav:\n  - modules/ROOT/nav.adoc\next:\n  collector:\n    - run:\n        command: ./mvnw validate process-resources dependency:unpack -am -Pantora-process-resources\n        local: true\n      scan:\n        - dir: target/classes/\n        - dir: target/antora/\n"
  },
  {
    "path": "src/main/antora/modules/ROOT/nav.adoc",
    "content": "* xref:index.adoc[Overview]\n* xref:keyvalue.adoc[]\n* xref:keyvalue/template.adoc[]\n\n* xref:repositories.adoc[]\n** xref:repositories/core-concepts.adoc[]\n** xref:repositories/definition.adoc[]\n** xref:repositories/create-instances.adoc[]\n** xref:keyvalue/repository/map-repositories.adoc[]\n** xref:keyvalue/value-expressions.adoc[]\n** xref:repositories/query-keywords-reference.adoc[]\n** xref:repositories/query-return-types-reference.adoc[]\n\n* xref:attachment$api/java/index.html[Javadoc,role=link-external,window=_blank]\n* https://github.com/spring-projects/spring-data-commons/wiki[Wiki,role=link-external,window=_blank]\n\n"
  },
  {
    "path": "src/main/antora/modules/ROOT/pages/commons/upgrade.adoc",
    "content": "include::{commons}@data-commons::page$upgrade.adoc[]\n\nOnce you’ve decided to upgrade your application, you can find detailed information regarding specific features in the rest of the document.\n\nSpring Data's documentation is specific to that version, so any information that you find in here will contain the most up-to-date changes that are in that version.\n"
  },
  {
    "path": "src/main/antora/modules/ROOT/pages/index.adoc",
    "content": "[[spring-data-key-value-reference-guide]]\n= Spring Data Key-Value\n:revnumber: {version}\n:revdate: {localdate}\n:feature-scroll: true\n\nSpring Data KeyValue provides connectivity and repository support for the in memory map structures.\nIt eases development of applications with a consistent programming model that need to access key based storage and servers as foundation for custom adapters._\n\n[horizontal]\nxref:keyvalue.adoc[Key/Value Storage] :: Support for built in key-value structures\nxref:repositories.adoc[Repositories] :: KeyValue Repositories\nhttps://github.com/spring-projects/spring-data-commons/wiki[Wiki] :: What's New, Upgrade Notes, Supported Versions, additional cross-version information.\n\nOliver Gierke; Thomas Darimont; Christoph Strobl; Jay Bryant; Mark Paluch\n\n(C) 2008-{copyright-year} VMware, Inc.\n\nCopies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.\n"
  },
  {
    "path": "src/main/antora/modules/ROOT/pages/keyvalue/repository/map-repositories.adoc",
    "content": "[[key-value.repositories.map]]\n= Map Repositories\n\nMap repositories reside on top of the `KeyValueTemplate`.\nUsing the default `PredicateQueryCreator` allows deriving query and sort expressions from the given method name, as the following example shows:\n\n[source, java]\n----\n@Configuration\n@EnableMapRepositories\nclass KeyValueConfig {\n\n}\n\ninterface PersonRepository implements CrudRepository<Person, String> {\n    List<Person> findByLastname(String lastname);\n}\n----\n\n== Configuring the QueryEngine\n\nIt is possible to change the `QueryEngine` and use a custom one instead of the default.\nThe `EnableMapRepositories` annotation allows to configure the by supplying a `QueryEngineFactory` as well as the `QueryCreator` via according attributes.\nPlease mind that the `QueryEngine` needs to be able to process queries created by the configured `QueryCreator`.\n\n== Storage Backend Configuration\n\n`KeySpaceStore` provides a simple storage facade that can be used along with the `keySpaceStoreRef` attribute of `EnableMapRepositories` to set the bean reference to the actual storage, overriding `mapType` settings.\n\n====\n[source,java]\n----\n@Configuration\n@EnableMapRepositories(keySpaceStoreRef = \"store\") <1>\npublic class MapDbConfiguration {\n\n\t@Bean\n\tKeySpaceStore store(DB db) { <1>\n\n\t\treturn new KeySpaceStore() {\n\n\t\t\t@Override\n\t\t\tpublic Map<Object, Object> getKeySpace(String keyspace) {\n\t\t\t\treturn db.hashMap(keyspace, ...\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void clear() {\n\t\t\t\t//\n\t\t\t}\n\n\t\t};\n\t}\n\n}\n----\n<1> reference the `KeySpaceStore` by bean name.\n====\n"
  },
  {
    "path": "src/main/antora/modules/ROOT/pages/keyvalue/template.adoc",
    "content": "[[key-value.template]]\n= Template API\n\nIn its very basic shape, the `KeyValueTemplate` uses a `MapAdapter` that wraps a `ConcurrentHashMap` and that uses link:{spring-framework-docs}/core/expressions.html[Spring Expression Language] to run queries and sorting.\n\nNOTE: The used `KeyValueAdapter` does the heavy lifting when it comes to storing and retrieving data.\nThe data structure influences performance and multi-threading behavior.\n\nYou can use a different type or pre-initialize the adapter with some values, and you can do so by using various constructors on `MapKeyValueAdapter`, as the following example shows:\n\n====\n[source, java]\n----\n@Configuration\nclass MyConfiguration {\n\n  @Bean\n  public KeyValueOperations mapKeyValueTemplate() {               <1>\n    return new KeyValueTemplate(keyValueAdapter());\n  }\n\n  @Bean\n  public KeyValueAdapter keyValueAdapter() {\n    return new MapKeyValueAdapter(ConcurrentHashMap.class);       <2>\n  }\n}\n----\n<1> Defines a custom `KeyValueOperations` bean using the default bean name. See documentation and properties of `@EnableMapRepositories` for further customization.\n<2> Defines a custom `KeyValueAdapter` bean using a `ConcurrentHashMap` as storage that is used by `KeyValueTemplate`.\n====\n\n[[key-value.keyspaces]]\n== Keyspaces\n\nThe following example shows a keyspace for a repository of `Person` objects:\n\n====\n[source, java]\n----\n@KeySpace(\"persons\")\nclass Person {\n\n  @Id String id;\n  String firstname;\n  String lastname;\n}\n\nclass User extends Person {\n  String username;\n}\n\ntemplate.findAllOf(Person.class); <1>\ntemplate.findAllOf(User.class);   <2>\n----\n<1> Returns all entities for the `persons` keyspace.\n<2> Returns only elements of type `User` stored in `persons` keyspace.\n====\n\nTIP: `@KeySpace` supports xref:keyvalue/value-expressions.adoc[Value Expressions] allowing dynamic keyspace configuration.\n\n[[key-value.keyspaces-custom]]\n=== Custom KeySpace Annotation\n\nYou can compose your own `KeySpace` annotations for a more domain-centric usage by annotating one of the attributes with `@AliasFor`.\n\nIMPORTANT: The composed annotation must inherit `@Persistent`.\n\nThe following example shows a custom `@KeySpace` annotation:\n\n====\n[source, java]\n----\n@KeySpace\n@Persistent\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ ElementType.TYPE })\nstatic @interface CacheCentricAnnotation {\n\n  @AliasFor(annotation = KeySpace.class, attribute = \"value\")\n  String cacheRegion() default \"\";\n}\n\n@CacheCentricAnnotation(cacheRegion = \"customers\")\nclass Customer {\n  //...\n}\n----\n====\n\n[[key-value.template-query]]\n== Querying\n\nRunning queries is managed by a `QueryEngine`.\nAs mentioned earlier, you can instruct the `KeyValueAdapter` to use an implementation-specific `QueryEngine` that allows access to native functionality.\nWhen used without further customization, queries can be run by using `SpELQueryEngine`.\n\nNOTE: For performance reasons, we highly recommend to make use of link:{spring-framework-docs}/core/expressions/evaluation.html#expressions-compiler-configuration[compiled SpEL Expressions].\n(\"`SpEL`\" is short for \"`Spring Expression Language`\".) You can use the `-Dspring.expression.compiler.mode=IMMEDIATE` switch to enable it.\n\nThe following example shows a query that uses the SpEL:\n\n====\n[source,java]\n----\nKeyValueQuery<String> query = new KeyValueQuery<String>(\"lastname == 'targaryen'\");\nList<Person> targaryens = template.find(query, Person.class);\n----\n====\n\nIMPORTANT: You must have getters and setters present to query properties when you use SpEL.\n\n[[key-value.template-sort]]\n== Sorting\n\nDepending on the store implementation provided by the adapter, entities might already be stored in some sorted way but do not necessarily have to be.Again, the underlying `QueryEngine` is capable of performing sort operations.\nWhen used without further customization, sorting is done by using a `SpelPropertyComparator` extracted from the `Sort` clause.The following example shows a query with a `Sort` clause:\n\n====\n[source, java]\n----\nKeyValueQuery<String> query = new KeyValueQuery<String>(\"lastname == 'baratheon'\");\nquery.setSort(Sort.by(DESC, \"age\"));\nList<Person> targaryens = template.find(query, Person.class);\n----\n====\n\nIMPORTANT: Please note that you need to have getters and setters present to sort using SpEL.\n"
  },
  {
    "path": "src/main/antora/modules/ROOT/pages/keyvalue/value-expressions.adoc",
    "content": "include::{commons}@data-commons::page$value-expressions.adoc[]\n"
  },
  {
    "path": "src/main/antora/modules/ROOT/pages/keyvalue.adoc",
    "content": "[[key-value]]\n= KeyValue\n\nSpring Data KeyValue provides easy configuration and access to `Map` like structures that associate values with unique keys.\nIt offers both low-level and high-level abstractions for interacting with the underlying data structure, freeing the user from infrastructural concerns.\n\nThe key-value abstraction within Spring Data Key Value requires an `Adapter` that shields the native store implementation, freeing up `KeyValueTemplate` to work on top of any key-value pair-like structure.\nKeys are distributed across <<key-value.keyspaces,Keyspaces>>.\nUnless otherwise specified, the class name is used as the default keyspace for an entity.\nThe following interface definition shows the `KeyValueOperations` interface, which is the heart of Spring Data Key-Value:\n\n====\n[source, java]\n----\ninterface KeyValueOperations {\n\n    <T> T insert(T objectToInsert);                               <1>\n\n    void update(Object objectToUpdate);                           <2>\n\n    void delete(Class<?> type);                                   <3>\n\n    <T> T findById(Object id, Class<T> type);                     <4>\n\n    <T> Iterable<T> findAllOf(Class<T> type);                     <5>\n\n    <T> Iterable<T> find(KeyValueQuery<?> query, Class<T> type);  <6>\n\n    //... more functionality omitted.\n\n}\n----\n<1> Inserts the given entity and assigns an ID (if required).\n<2> Updates the given entity.\n<3> Removes all entities of the matching type.\n<4> Returns the entity of the given type with its matching ID.\n<5> Returns all entities of the matching type.\n<6> Returns a `List` of all entities of the given type that match the criteria of the query.\n====\n\n[[key-value.keyspaces]]\n== Keyspaces\n\nKeyspaces define the part of the data structure in which the entity should be kept.\nThis concept is similar to collections in MongoDB and Elasticsearch, cores in Solr, and tables in JPA.\nBy default, the keyspace of an entity is extracted from its type, but you can also store entities of different types within one keyspace.\n"
  },
  {
    "path": "src/main/antora/modules/ROOT/pages/repositories/core-concepts.adoc",
    "content": "include::{commons}@data-commons::page$repositories/core-concepts.adoc[]\n\n[[redis.entity-persistence.state-detection-strategies]]\ninclude::{commons}@data-commons::page$is-new-state-detection.adoc[leveloffset=+1]\n"
  },
  {
    "path": "src/main/antora/modules/ROOT/pages/repositories/core-domain-events.adoc",
    "content": "include::{commons}@data-commons::page$repositories/core-domain-events.adoc[]\n"
  },
  {
    "path": "src/main/antora/modules/ROOT/pages/repositories/core-extensions.adoc",
    "content": "[[core.extensions.querydsl]]\n= Querydsl\n\nSpring Data Redis does not support Querydsl.\n"
  },
  {
    "path": "src/main/antora/modules/ROOT/pages/repositories/create-instances.adoc",
    "content": "include::{commons}@data-commons::page$repositories/create-instances.adoc[]\n"
  },
  {
    "path": "src/main/antora/modules/ROOT/pages/repositories/custom-implementations.adoc",
    "content": "include::{commons}@data-commons::page$repositories/custom-implementations.adoc[]\n"
  },
  {
    "path": "src/main/antora/modules/ROOT/pages/repositories/definition.adoc",
    "content": "include::{commons}@data-commons::page$repositories/definition.adoc[]\n"
  },
  {
    "path": "src/main/antora/modules/ROOT/pages/repositories/null-handling.adoc",
    "content": "include::{commons}@data-commons::page$repositories/null-handling.adoc[]\n"
  },
  {
    "path": "src/main/antora/modules/ROOT/pages/repositories/object-mapping.adoc",
    "content": "include::{commons}@data-commons::page$object-mapping.adoc[]\n"
  },
  {
    "path": "src/main/antora/modules/ROOT/pages/repositories/projections.adoc",
    "content": "[[cassandra.projections]]\n= Projections\n\ninclude::{commons}@data-commons::page$repositories/projections.adoc[leveloffset=+1]\n"
  },
  {
    "path": "src/main/antora/modules/ROOT/pages/repositories/query-keywords-reference.adoc",
    "content": "include::{commons}@data-commons::page$repositories/query-keywords-reference.adoc[]\n"
  },
  {
    "path": "src/main/antora/modules/ROOT/pages/repositories/query-methods-details.adoc",
    "content": "include::{commons}@data-commons::page$repositories/query-methods-details.adoc[]\n"
  },
  {
    "path": "src/main/antora/modules/ROOT/pages/repositories/query-return-types-reference.adoc",
    "content": "include::{commons}@data-commons::page$repositories/query-return-types-reference.adoc[]\n"
  },
  {
    "path": "src/main/antora/modules/ROOT/pages/repositories.adoc",
    "content": "[[keyvalyue.repositories]]\n= KeyValue Repositories\n\nThis chapter explains the basic foundations of Spring Data repositories and KeyValue specifics.\nBefore continuing to the specifics, make sure you have a sound understanding of the basic concepts.\n\nThe goal of the Spring Data repository abstraction is to significantly reduce the amount of boilerplate code required to implement data access layers for various persistence stores.\n"
  },
  {
    "path": "src/main/antora/resources/antora-resources/antora.yml",
    "content": "version: ${antora-component.version}\nprerelease: ${antora-component.prerelease}\n\nasciidoc:\n  attributes:\n    attribute-missing: 'warn'\n    chomp: 'all'\n    version: '${project.version}'\n    copyright-year: '${current.year}'\n    springversionshort: '${spring.short}'\n    springversion: '${spring}'\n    commons: '${springdata.commons.docs}'\n    include-xml-namespaces: false\n    spring-data-commons-docs-url: '${documentation.baseurl}/spring-data/commons/reference/${springdata.commons.short}'\n    spring-data-commons-javadoc-base: '{spring-data-commons-docs-url}/api/java'\n    springdocsurl: '${documentation.baseurl}/spring-framework/reference/{springversionshort}'\n    spring-framework-docs: '{springdocsurl}'\n    springjavadocurl: '${documentation.spring-javadoc-url}'\n    spring-framework-javadoc: '{springjavadocurl}'\n    springhateoasversion: '${spring-hateoas}'\n    releasetrainversion: '${releasetrain}'\n    store: KeyValue\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/annotation/KeySpace.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.annotation;\n\nimport static java.lang.annotation.ElementType.*;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.data.annotation.Persistent;\n\n/**\n * Marker interface for methods with {@link Persistent} annotations indicating the presence of a dedicated keyspace the\n * entity should reside in. If present the value will be picked up for resolving the keyspace. The {@link #value()}\n * attribute supports Value Expressions to dynamically resolve the keyspace based on a per-operation basis.\n *\n * <pre class=\"code\">\n * &#64;Persistent\n * &#64;Retention(RetentionPolicy.RUNTIME)\n * &#64;Target({ ElementType.TYPE })\n * static @interface CacheCentricAnnotation {\n *\n * \t&#64;AliasFor(annotation = KeySpace.class, attribute = \"value\")\n * \tString cacheRegion() default \"\";\n * }\n *\n * &#64;CacheCentricAnnotation(cacheRegion = \"customers\")\n * class Customer {\n * \t// ...\n * }\n * </pre>\n *\n * Can also be directly used on types to indicate the keyspace.\n *\n * <pre class=\"code\">\n * &#64;KeySpace(\"persons\")\n * public class Foo {\n *\n * }\n * </pre>\n *\n * @author Christoph Strobl\n * @author Mark Paluch\n */\n@Documented\n@Retention(RetentionPolicy.RUNTIME)\n@Target(value = { METHOD, TYPE })\npublic @interface KeySpace {\n\n\t/**\n\t * @return dedicated keyspace the entity should reside in.\n\t */\n\tString value() default \"\";\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/annotation/package-info.java",
    "content": "/**\n * Key-Value annotations for declarative keyspace configuration.\n */\n@org.jspecify.annotations.NullMarked\npackage org.springframework.data.keyvalue.annotation;\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/aot/KeyValueRuntimeHints.java",
    "content": "/*\n * Copyright 2022-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.aot;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.aot.hint.ExecutableMode;\nimport org.springframework.aot.hint.MemberCategory;\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.aot.hint.TypeReference;\nimport org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery;\n\n/**\n * {@link RuntimeHintsRegistrar} for KeyValue.\n *\n * @author Christoph Strobl\n * @since 3.0\n */\nclass KeyValueRuntimeHints implements RuntimeHintsRegistrar {\n\n\t@Override\n\tpublic void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {\n\n\t\t// REFLECTION\n\t\thints.reflection().registerTypes(\n\t\t\t\tArrays.asList(\n\t\t\t\t\t\tTypeReference.of(org.springframework.data.keyvalue.repository.support.SimpleKeyValueRepository.class),\n\t\t\t\t\t\tTypeReference.of(KeyValuePartTreeQuery.class)),\n\t\t\t\thint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS));\n\n\t\thints.reflection().registerType(TypeReference.of(\"java.util.Comparators.NaturalOrderComparator\"),\n\t\t\t\tbuilder -> builder.withMethod(\"compare\",\n\t\t\t\t\t\tList.of(TypeReference.of(Object.class), TypeReference.of(Object.class)), ExecutableMode.INVOKE));\n\n\t\thints.reflection().registerType(TypeReference.of(\"java.util.Comparators.NullComparator\"),\n\t\t\t\tbuilder -> builder.withMethod(\"compare\",\n\t\t\t\t\t\tList.of(TypeReference.of(Object.class), TypeReference.of(Object.class)), ExecutableMode.INVOKE));\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/aot/package-info.java",
    "content": "/**\n * Support classes for key-value ahead of time computation\n */\n@org.jspecify.annotations.NullMarked\npackage org.springframework.data.keyvalue.aot;\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/AbstractKeyValueAdapter.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport java.util.Collection;\nimport java.util.Comparator;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.data.keyvalue.core.query.KeyValueQuery;\n\n/**\n * Base implementation of {@link KeyValueAdapter} holds {@link QueryEngine} to delegate {@literal find} and\n * {@literal count} execution to.\n *\n * @author Christoph Strobl\n * @author Mark Paluch\n */\npublic abstract class AbstractKeyValueAdapter implements KeyValueAdapter {\n\n\tprivate final QueryEngine<? extends KeyValueAdapter, ?, ?> engine;\n\n\t/**\n\t * Creates new {@link AbstractKeyValueAdapter} with using the default query engine.\n\t */\n\tprotected AbstractKeyValueAdapter() {\n\t\tthis((QueryEngine<? extends KeyValueAdapter, ?, ?>) null);\n\t}\n\n\t/**\n\t * Creates new {@link AbstractKeyValueAdapter} with using the default query engine and provided comparator for sorting.\n\t *\n\t * @param sortAccessor must not be {@literal null}.\n\t * @since 3.1.10\n\t */\n\tprotected AbstractKeyValueAdapter(SortAccessor<Comparator<?>> sortAccessor) {\n\t\tthis(new PredicateQueryEngine(sortAccessor));\n\t}\n\n\t/**\n\t * Creates new {@link AbstractKeyValueAdapter} with using the default query engine.\n\t *\n\t * @param engine will be defaulted to {@link SpelQueryEngine} if {@literal null}.\n\t */\n\tprotected AbstractKeyValueAdapter(@Nullable QueryEngine<? extends KeyValueAdapter, ?, ?> engine) {\n\n\t\tthis.engine = engine != null ? engine : new PredicateQueryEngine();\n\t\tthis.engine.registerAdapter(this);\n\t}\n\n\t/**\n\t * Get the {@link QueryEngine} used.\n\t *\n\t * @return\n\t */\n\tprotected QueryEngine<? extends KeyValueAdapter, ?, ?> getQueryEngine() {\n\t\treturn engine;\n\t}\n\n\t@Override\n\tpublic <T> @Nullable T get(Object id, String keyspace, Class<T> type) {\n\t\treturn type.cast(get(id, keyspace));\n\t}\n\n\t@Override\n\tpublic <T> @Nullable T delete(Object id, String keyspace, Class<T> type) {\n\t\treturn type.cast(delete(id, keyspace));\n\t}\n\n\t@Override\n\tpublic <T> Iterable<T> find(KeyValueQuery<?> query, String keyspace, Class<T> type) {\n\t\treturn engine.execute(query, keyspace, type);\n\t}\n\n\t@Override\n\tpublic Collection<?> find(KeyValueQuery<?> query, String keyspace) {\n\t\treturn engine.execute(query, keyspace);\n\t}\n\n\t@Override\n\tpublic long count(KeyValueQuery<?> query, String keyspace) {\n\t\treturn engine.count(query, keyspace);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/CriteriaAccessor.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.data.keyvalue.core.query.KeyValueQuery;\n\n/**\n * Resolves the criteria object from given {@link KeyValueQuery}.\n *\n * @author Christoph Strobl\n * @author Mark Paluch\n * @param <T>\n */\npublic interface CriteriaAccessor<T> {\n\n\t/**\n\t * Checks and reads {@link KeyValueQuery#getCriteria()} of given {@link KeyValueQuery}. Might also apply additional\n\t * transformation to match the desired type.\n\t *\n\t * @param query must not be {@literal null}.\n\t * @return the criteria extracted from the query. Can be {@literal null}.\n\t * @throws IllegalArgumentException in case the criteria is not valid for usage with specific\n\t *           {@link CriteriaAccessor}.\n\t */\n\t@Nullable\n\tT resolve(KeyValueQuery<?> query);\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/DefaultIdentifierGenerator.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport java.security.NoSuchAlgorithmException;\nimport java.security.SecureRandom;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.UUID;\nimport java.util.concurrent.atomic.AtomicReference;\n\nimport org.springframework.dao.InvalidDataAccessApiUsageException;\nimport org.springframework.data.core.TypeInformation;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * Default implementation of {@link IdentifierGenerator} to generate identifiers of types {@link UUID}, String,\n *\n * @author Christoph Strobl\n * @author Oliver Gierke\n */\nenum DefaultIdentifierGenerator implements IdentifierGenerator {\n\n\tINSTANCE;\n\n\tprivate final AtomicReference<SecureRandom> secureRandom = new AtomicReference<>(null);\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T> T generateIdentifierOfType(TypeInformation<T> identifierType) {\n\n\t\tClass<?> type = identifierType.getType();\n\n\t\tif (ClassUtils.isAssignable(UUID.class, type)) {\n\t\t\treturn (T) UUID.randomUUID();\n\t\t} else if (ClassUtils.isAssignable(String.class, type)) {\n\t\t\treturn (T) UUID.randomUUID().toString();\n\t\t} else if (ClassUtils.isAssignable(Integer.class, type)) {\n\t\t\treturn (T) Integer.valueOf(getSecureRandom().nextInt());\n\t\t} else if (ClassUtils.isAssignable(Long.class, type)) {\n\t\t\treturn (T) Long.valueOf(getSecureRandom().nextLong());\n\t\t}\n\n\t\tthrow new InvalidDataAccessApiUsageException(\n\t\t\t\tString.format(\"Identifier cannot be generated for %s; Supported types are: UUID, String, Integer, and Long\",\n\t\t\t\t\t\tidentifierType.getType().getName()));\n\t}\n\n\tprivate SecureRandom getSecureRandom() {\n\n\t\tSecureRandom secureRandom = this.secureRandom.get();\n\t\tif (secureRandom != null) {\n\t\t\treturn secureRandom;\n\t\t}\n\n\t\tfor (String algorithm : OsTools.secureRandomAlgorithmNames()) {\n\t\t\ttry {\n\t\t\t\tsecureRandom = SecureRandom.getInstance(algorithm);\n\t\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\t\t// ignore and try next.\n\t\t\t}\n\t\t}\n\n\t\tif (secureRandom == null) {\n\t\t\tthrow new InvalidDataAccessApiUsageException(\n\t\t\t\t\tString.format(\"Could not create SecureRandom instance for one of the algorithms '%s'\",\n\t\t\t\t\t\t\tStringUtils.collectionToCommaDelimitedString(OsTools.secureRandomAlgorithmNames())));\n\t\t}\n\n\t\tthis.secureRandom.compareAndSet(null, secureRandom);\n\n\t\treturn secureRandom;\n\t}\n\n\t/**\n\t * @author Christoph Strobl\n\t * @since 1.1.2\n\t */\n\tprivate static class OsTools {\n\n\t\tprivate static final String OPERATING_SYSTEM_NAME = System.getProperty(\"os.name\").toLowerCase();\n\n\t\tprivate static final List<String> SECURE_RANDOM_ALGORITHMS_LINUX_OSX_SOLARIS = Arrays.asList(\"NativePRNGBlocking\",\n\t\t\t\t\"NativePRNGNonBlocking\", \"NativePRNG\", \"SHA1PRNG\");\n\t\tprivate static final List<String> SECURE_RANDOM_ALGORITHMS_WINDOWS = Arrays.asList(\"SHA1PRNG\", \"Windows-PRNG\");\n\n\t\tstatic List<String> secureRandomAlgorithmNames() {\n\t\t\treturn OPERATING_SYSTEM_NAME.contains(\"win\") ? SECURE_RANDOM_ALGORITHMS_WINDOWS\n\t\t\t\t\t: SECURE_RANDOM_ALGORITHMS_LINUX_OSX_SOLARIS;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/ForwardingCloseableIterator.java",
    "content": "/*\n * Copyright 2015-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport java.util.Iterator;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.data.util.CloseableIterator;\nimport org.springframework.util.Assert;\n\n/**\n * Forwards {@link CloseableIterator} invocations to the configured {@link Iterator} delegate.\n *\n * @author Christoph Strobl\n * @author Thomas Darimont\n * @author Oliver Gierke\n * @author Mark Paluch\n */\npublic class ForwardingCloseableIterator<T> implements CloseableIterator<T> {\n\n\tprivate final Iterator<? extends T> delegate;\n\tprivate final @Nullable Runnable closeHandler;\n\n\t/**\n\t * Creates a new {@link ForwardingCloseableIterator}.\n\t *\n\t * @param delegate must not be {@literal null}.\n\t */\n\tpublic ForwardingCloseableIterator(Iterator<? extends T> delegate) {\n\t\tthis(delegate, null);\n\t}\n\n\t/**\n\t * Creates a new {@link ForwardingCloseableIterator} that invokes the configured {@code closeHandler} on\n\t * {@link #close()}.\n\t *\n\t * @param delegate must not be {@literal null}.\n\t * @param closeHandler may be {@literal null}.\n\t */\n\tpublic ForwardingCloseableIterator(Iterator<? extends T> delegate, @Nullable Runnable closeHandler) {\n\n\t\tAssert.notNull(delegate, \"Delegate iterator must not be null\");\n\n\t\tthis.delegate = delegate;\n\t\tthis.closeHandler = closeHandler;\n\t}\n\n\t@Override\n\tpublic boolean hasNext() {\n\t\treturn delegate.hasNext();\n\t}\n\n\t@Override\n\tpublic T next() {\n\t\treturn delegate.next();\n\t}\n\n\t@Override\n\tpublic void close() {\n\t\tif (closeHandler != null) {\n\t\t\tcloseHandler.run();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/GeneratingIdAccessor.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.data.mapping.IdentifierAccessor;\nimport org.springframework.data.mapping.PersistentProperty;\nimport org.springframework.data.mapping.PersistentPropertyAccessor;\nimport org.springframework.util.Assert;\n\n/**\n * {@link IdentifierAccessor} adding a {@link #getOrGenerateIdentifier()} to automatically generate an identifier and\n * set it on the underling bean instance.\n *\n * @author Oliver Gierke\n * @author Mark Paluch\n * @see #getOrGenerateIdentifier()\n */\nclass GeneratingIdAccessor implements IdentifierAccessor {\n\n\tprivate final PersistentPropertyAccessor<?> accessor;\n\tprivate final PersistentProperty<?> identifierProperty;\n\tprivate final IdentifierGenerator generator;\n\n\t/**\n\t * Creates a new {@link GeneratingIdAccessor} using the given {@link PersistentPropertyAccessor}, identifier property\n\t * and {@link IdentifierGenerator}.\n\t *\n\t * @param accessor must not be {@literal null}.\n\t * @param identifierProperty must not be {@literal null}.\n\t * @param generator must not be {@literal null}.\n\t */\n\tGeneratingIdAccessor(PersistentPropertyAccessor<?> accessor, PersistentProperty<?> identifierProperty,\n\t\t\tIdentifierGenerator generator) {\n\n\t\tAssert.notNull(accessor, \"PersistentPropertyAccessor must not be null\");\n\t\tAssert.notNull(identifierProperty, \"Identifier property must not be null\");\n\t\tAssert.notNull(generator, \"IdentifierGenerator must not be null\");\n\n\t\tthis.accessor = accessor;\n\t\tthis.identifierProperty = identifierProperty;\n\t\tthis.generator = generator;\n\t}\n\n\t@Override\n\tpublic @Nullable Object getIdentifier() {\n\t\treturn accessor.getProperty(identifierProperty);\n\t}\n\n\t/**\n\t * Returns the identifier value of the backing bean or generates a new one using the configured\n\t * {@link IdentifierGenerator}.\n\t *\n\t * @return\n\t */\n\tObject getOrGenerateIdentifier() {\n\n\t\tObject existingIdentifier = getIdentifier();\n\n\t\tif (existingIdentifier != null) {\n\t\t\treturn existingIdentifier;\n\t\t}\n\n\t\tObject generatedIdentifier = generator.generateIdentifierOfType(identifierProperty.getTypeInformation());\n\t\taccessor.setProperty(identifierProperty, generatedIdentifier);\n\n\t\treturn generatedIdentifier;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/IdentifierGenerator.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport org.springframework.data.core.TypeInformation;\n\n/**\n * API for components generating identifiers.\n *\n * @author Christoph Strobl\n * @author Oliver Gierke\n */\npublic interface IdentifierGenerator {\n\n\t/**\n\t * Creates an identifier of the given type.\n\t *\n\t * @param type must not be {@literal null}.\n\t * @return an identifier of the given type.\n\t */\n\t<T> T generateIdentifierOfType(TypeInformation<T> type);\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/IterableConverter.java",
    "content": "/*\n * Copyright 2015-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.lang.Contract;\n\n/**\n * Converter capable of transforming a given {@link Iterable} into a collection type.\n *\n * @author Christoph Strobl\n * @author Mark Paluch\n */\npublic final class IterableConverter {\n\n\tprivate IterableConverter() {}\n\n\t/**\n\t * Converts a given {@link Iterable} into a {@link List}\n\t *\n\t * @param source\n\t * @return {@link Collections#emptyList()} when source is {@literal null}.\n\t */\n\t@Contract(\"_ -> !null\")\n\tpublic static <T> List<T> toList(@Nullable Iterable<T> source) {\n\n\t\tif(source == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\n\t\tif (source instanceof List) {\n\t\t\treturn (List<T>) source;\n\t\t}\n\n\t\tif (source instanceof Collection) {\n\t\t\treturn new ArrayList<>((Collection<T>) source);\n\t\t}\n\n\t\tList<T> result = new ArrayList<>();\n\t\tfor (T value : source) {\n\t\t\tresult.add(value);\n\t\t}\n\t\treturn result;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport java.util.Collection;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.data.keyvalue.core.query.KeyValueQuery;\nimport org.springframework.data.util.CloseableIterator;\n\n/**\n * {@link KeyValueAdapter} unifies access and shields the underlying key/value specific implementation.\n *\n * @author Christoph Strobl\n * @author Thomas Darimont\n * @author Mark Paluch\n */\npublic interface KeyValueAdapter extends DisposableBean {\n\n\t/**\n\t * Add object with given id to keyspace.\n\t *\n\t * @param id must not be {@literal null}.\n\t * @param keyspace must not be {@literal null}.\n\t * @return the item previously associated with the id.\n\t */\n\t@Nullable Object put(Object id, Object item, String keyspace);\n\n\t/**\n\t * Check if a object with given id exists in keyspace.\n\t *\n\t * @param id must not be {@literal null}.\n\t * @param keyspace must not be {@literal null}.\n\t * @return true if item of type with id exists.\n\t */\n\tboolean contains(Object id, String keyspace);\n\n\t/**\n\t * Get the object with given id from keyspace.\n\t *\n\t * @param id must not be {@literal null}.\n\t * @param keyspace must not be {@literal null}.\n\t * @return {@literal null} in case no matching item exists.\n\t */\n\t@Nullable\n\tObject get(Object id, String keyspace);\n\n\t/**\n\t * Get the object with given id from keyspace.\n\t *\n\t * @param id must not be {@literal null}.\n\t * @param keyspace must not be {@literal null}.\n\t * @param type must not be {@literal null}.\n\t * @return {@literal null} in case no matching item exists.\n\t * @since 1.1\n\t */\n\t<T> @Nullable T get(Object id, String keyspace, Class<T> type);\n\n\t/**\n\t * Delete and return the object with given type and id.\n\t *\n\t * @param id must not be {@literal null}.\n\t * @param keyspace must not be {@literal null}.\n\t * @return {@literal null} if object could not be found\n\t */\n\t@Nullable\n\tObject delete(Object id, String keyspace);\n\n\t/**\n\t * Delete and return the object with given type and id.\n\t *\n\t * @param id must not be {@literal null}.\n\t * @param keyspace must not be {@literal null}.\n\t * @param type must not be {@literal null}.\n\t * @return {@literal null} if object could not be found\n\t * @since 1.1\n\t */\n\t<T> @Nullable T delete(Object id, String keyspace, Class<T> type);\n\n\t/**\n\t * Get all elements for given keyspace.\n\t *\n\t * @param keyspace must not be {@literal null}.\n\t * @return empty {@link Collection} if nothing found.\n\t */\n\tIterable<Object> getAllOf(String keyspace);\n\n\t/**\n\t * Get all elements for given keyspace.\n\t *\n\t * @param keyspace must not be {@literal null}.\n\t * @param type must not be {@literal null}.\n\t * @return empty {@link Collection} if nothing found.\n\t * @since 2.5\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tdefault <T> Iterable<T> getAllOf(String keyspace, Class<T> type) {\n\t\treturn (Iterable<T>) getAllOf(keyspace);\n\t}\n\n\t/**\n\t * Returns a {@link CloseableIterator} that iterates over all entries.\n\t *\n\t * @param keyspace must not be {@literal null}.\n\t * @return\n\t */\n\tCloseableIterator<Map.Entry<Object, Object>> entries(String keyspace);\n\n\t/**\n\t * Returns a {@link CloseableIterator} that iterates over all entries.\n\t *\n\t * @param keyspace must not be {@literal null}.\n\t * @param type must not be {@literal null}.\n\t * @return\n\t * @since 2.5\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tdefault <T> CloseableIterator<Map.Entry<Object,T>> entries(String keyspace, Class<T> type) {\n\t\treturn (CloseableIterator) entries(keyspace);\n\t}\n\n\t/**\n\t * Remove all objects of given type.\n\t *\n\t * @param keyspace must not be {@literal null}.\n\t */\n\tvoid deleteAllOf(String keyspace);\n\n\t/**\n\t * Removes all objects.\n\t */\n\tvoid clear();\n\n\t/**\n\t * Find all matching objects within {@literal keyspace}.\n\t *\n\t * @param query must not be {@literal null}.\n\t * @param keyspace must not be {@literal null}.\n\t * @return empty {@link Collection} if no match found.\n\t */\n\tdefault Iterable<?> find(KeyValueQuery<?> query, String keyspace) {\n\t\treturn find(query, keyspace, Object.class);\n\t}\n\n\t/**\n\t * @param query must not be {@literal null}.\n\t * @param keyspace must not be {@literal null}.\n\t * @param type must not be {@literal null}.\n\t * @return empty {@link Collection} if no match found.\n\t * @since 1.1\n\t */\n\t<T> Iterable<T> find(KeyValueQuery<?> query, String keyspace, Class<T> type);\n\n\t/**\n\t * Count number of objects within {@literal keyspace}.\n\t *\n\t * @param keyspace must not be {@literal null}.\n\t * @return\n\t */\n\tlong count(String keyspace);\n\n\t/**\n\t * Count all matching objects within {@literal keyspace}.\n\t *\n\t * @param query must not be {@literal null}.\n\t * @param keyspace must not be {@literal null}.\n\t * @return\n\t */\n\tlong count(KeyValueQuery<?> query, String keyspace);\n\n\t/**\n\t * Determine whether result of given {@link KeyValueQuery} within {@literal keyspace} contains at least one element.\n\t *\n\t * @param query must not be {@literal null}.\n\t * @param keyspace must not be {@literal null}.\n\t * @return\n\t * @since 2.7\n\t */\n\tdefault boolean exists(KeyValueQuery<?> query, String keyspace) {\n\t\treturn count(query, keyspace) > 0;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/KeyValueCallback.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Generic callback interface for code that operates on a {@link KeyValueAdapter}. This is particularly useful for\n * delegating code that needs to work closely on the underlying key/value store implementation.\n *\n * @author Christoph Strobl\n * @author Mark Paluch\n * @param <T>\n */\npublic interface KeyValueCallback<T> {\n\n\t/**\n\t * Gets called by {@code KeyValueTemplate#execute(KeyValueCallback)}. Allows for returning a result object created\n\t * within the callback, i.e. a domain object or a collection of domain objects.\n\t *\n\t * @param adapter\n\t * @return\n\t */\n\t@Nullable\n\tT doInKeyValue(KeyValueAdapter adapter);\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/KeyValueOperations.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport java.util.Optional;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.data.domain.Sort;\nimport org.springframework.data.keyvalue.annotation.KeySpace;\nimport org.springframework.data.keyvalue.core.query.KeyValueQuery;\nimport org.springframework.data.mapping.context.MappingContext;\n\n/**\n * Interface that specifies a basic set of key/value operations. Implemented by {@link KeyValueTemplate}.\n *\n * @author Christoph Strobl\n * @author Mark Paluch\n */\npublic interface KeyValueOperations extends DisposableBean {\n\n\t/**\n\t * Add given object. Object needs to have id property to which a generated value will be assigned.\n\t *\n\t * @param objectToInsert\n\t * @return the inserted object.\n\t */\n\t<T> T insert(T objectToInsert);\n\n\t/**\n\t * Add object with given id.\n\t *\n\t * @param id must not be {@literal null}.\n\t * @param objectToInsert must not be {@literal null}.\n\t * @return the inserted object.\n\t */\n\t<T> T insert(Object id, T objectToInsert);\n\n\t/**\n\t * Get all elements of given type. Respects {@link KeySpace} if present and therefore returns all elements that can be\n\t * assigned to requested type.\n\t *\n\t * @param type must not be {@literal null}.\n\t * @return empty iterable if no elements found.\n\t */\n\t<T> Iterable<T> findAll(Class<T> type);\n\n\t/**\n\t * Get all elements ordered by sort. Respects {@link KeySpace} if present and therefore returns all elements that can\n\t * be assigned to requested type.\n\t *\n\t * @param sort must not be {@literal null}.\n\t * @param type must not be {@literal null}.\n\t * @return\n\t */\n\t<T> Iterable<T> findAll(Sort sort, Class<T> type);\n\n\t/**\n\t * Get element of given type with given id. Respects {@link KeySpace} if present and therefore returns all elements\n\t * that can be assigned to requested type.\n\t *\n\t * @param id must not be {@literal null}.\n\t * @param type must not be {@literal null}.\n\t * @return {@link Optional#empty()} if not found.\n\t */\n\t<T> Optional<T> findById(Object id, Class<T> type);\n\n\t/**\n\t * Execute operation against underlying store.\n\t *\n\t * @param action must not be {@literal null}.\n\t * @return\n\t */\n\n\t<T> @Nullable T execute(KeyValueCallback<T> action);\n\n\t/**\n\t * Get all elements matching the given query. <br />\n\t * Respects {@link KeySpace} if present and therefore returns all elements that can be assigned to requested type..\n\t *\n\t * @param query must not be {@literal null}.\n\t * @param type must not be {@literal null}.\n\t * @return empty iterable if no match found.\n\t */\n\t<T> Iterable<T> find(KeyValueQuery<?> query, Class<T> type);\n\n\t/**\n\t * Get all elements in given range. Respects {@link KeySpace} if present and therefore returns all elements that can\n\t * be assigned to requested type.\n\t *\n\t * @param offset\n\t * @param rows\n\t * @param type must not be {@literal null}.\n\t * @return\n\t */\n\t<T> Iterable<T> findInRange(long offset, int rows, Class<T> type);\n\n\t/**\n\t * Get all elements in given range ordered by sort. Respects {@link KeySpace} if present and therefore returns all\n\t * elements that can be assigned to requested type.\n\t *\n\t * @param offset\n\t * @param rows\n\t * @param sort\n\t * @param type\n\t * @return\n\t */\n\t<T> Iterable<T> findInRange(long offset, int rows, Sort sort, Class<T> type);\n\n\t/**\n\t * @param objectToUpdate must not be {@literal null}.\n\t * @return the updated object.\n\t */\n\t<T> T update(T objectToUpdate);\n\n\t/**\n\t * @param id must not be {@literal null}.\n\t * @param objectToUpdate must not be {@literal null}.\n\t * @return the updated object.\n\t */\n\t<T> T update(Object id, T objectToUpdate);\n\n\t/**\n\t * Remove all elements of type. Respects {@link KeySpace} if present and therefore removes all elements that can be\n\t * assigned to requested type.\n\t *\n\t * @param type must not be {@literal null}.\n\t */\n\tvoid delete(Class<?> type);\n\n\t/**\n\t * @param objectToDelete must not be {@literal null}.\n\t * @return\n\t */\n\t<T> @Nullable T delete(T objectToDelete);\n\n\t/**\n\t * Delete item of type with given id.\n\t *\n\t * @param id must not be {@literal null}.\n\t * @param type must not be {@literal null}.\n\t * @return the deleted item or {@literal null} if no match found.\n\t */\n\t<T> @Nullable T delete(Object id, Class<T> type);\n\n\t/**\n\t * Total number of elements with given type available. Respects {@link KeySpace} if present and therefore counts all\n\t * elements that can be assigned to requested type.\n\t *\n\t * @param type must not be {@literal null}.\n\t * @return\n\t */\n\tlong count(Class<?> type);\n\n\t/**\n\t * Total number of elements matching given query. Respects {@link KeySpace} if present and therefore counts all\n\t * elements that can be assigned to requested type.\n\t *\n\t * @param query\n\t * @param type\n\t * @return\n\t */\n\tlong count(KeyValueQuery<?> query, Class<?> type);\n\n\t/**\n\t * Determine whether result of given {@link KeyValueQuery} contains at least one element.\n\t *\n\t * @param query\n\t * @param type\n\t * @return\n\t * @since 2.7\n\t */\n\tboolean exists(KeyValueQuery<?> query, Class<?> type);\n\n\t/**\n\t * @return mapping context in use.\n\t */\n\tMappingContext<?, ?> getMappingContext();\n\n\t/**\n\t * @return {@link KeyValueAdapter} in use.\n\t * @since 3.2.4\n\t */\n\tKeyValueAdapter getKeyValueAdapter();\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/KeyValuePersistenceExceptionTranslator.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport java.util.NoSuchElementException;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.dao.DataAccessException;\nimport org.springframework.dao.DataRetrievalFailureException;\nimport org.springframework.dao.support.PersistenceExceptionTranslator;\nimport org.springframework.util.Assert;\n\n/**\n * Simple {@link PersistenceExceptionTranslator} implementation for key/value stores that converts the given runtime\n * exception to an appropriate exception from the {@code org.springframework.dao} hierarchy.\n *\n * @author Christoph Strobl\n * @author Mark Paluch\n */\npublic class KeyValuePersistenceExceptionTranslator implements PersistenceExceptionTranslator {\n\n\t@Override\n\t@SuppressWarnings(\"NullAway\")\n\tpublic @Nullable DataAccessException translateExceptionIfPossible(RuntimeException exception) {\n\n\t\tAssert.notNull(exception, \"Exception must not be null\");\n\n\t\tif (exception instanceof DataAccessException) {\n\t\t\treturn (DataAccessException) exception;\n\t\t}\n\n\t\tif (exception instanceof NoSuchElementException || exception instanceof IndexOutOfBoundsException\n\t\t\t\t|| exception instanceof IllegalStateException) {\n\t\t\treturn new DataRetrievalFailureException(exception.getMessage(), exception);\n\t\t}\n\n\t\tif (exception.getClass().getName().startsWith(\"java\")) {\n\t\t\treturn new UncategorizedKeyValueException(exception.getMessage(), exception);\n\t\t}\n\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.context.ApplicationEventPublisherAware;\nimport org.springframework.dao.DataAccessException;\nimport org.springframework.dao.DuplicateKeyException;\nimport org.springframework.dao.InvalidDataAccessApiUsageException;\nimport org.springframework.dao.support.PersistenceExceptionTranslator;\nimport org.springframework.data.domain.Sort;\nimport org.springframework.data.keyvalue.core.event.KeyValueEvent;\nimport org.springframework.data.keyvalue.core.mapping.KeyValuePersistentEntity;\nimport org.springframework.data.keyvalue.core.mapping.KeyValuePersistentProperty;\nimport org.springframework.data.keyvalue.core.mapping.context.KeyValueMappingContext;\nimport org.springframework.data.keyvalue.core.query.KeyValueQuery;\nimport org.springframework.data.mapping.context.MappingContext;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * Basic implementation of {@link KeyValueOperations}.\n *\n * @author Christoph Strobl\n * @author Oliver Gierke\n * @author Thomas Darimont\n * @author Mark Paluch\n * @author Mert Zeybekler\n * @author Adeyemi Abass\n */\npublic class KeyValueTemplate implements KeyValueOperations, ApplicationEventPublisherAware {\n\n\tprivate static final PersistenceExceptionTranslator DEFAULT_PERSISTENCE_EXCEPTION_TRANSLATOR = new KeyValuePersistenceExceptionTranslator();\n\n\tprivate final KeyValueAdapter adapter;\n\tprivate final MappingContext<? extends KeyValuePersistentEntity<?, ?>, ? extends KeyValuePersistentProperty<?>> mappingContext;\n\tprivate final IdentifierGenerator identifierGenerator;\n\n\tprivate PersistenceExceptionTranslator exceptionTranslator = DEFAULT_PERSISTENCE_EXCEPTION_TRANSLATOR;\n\tprivate @Nullable ApplicationEventPublisher eventPublisher;\n\tprivate boolean publishEvents = true;\n\tprivate @SuppressWarnings(\"rawtypes\") Set<Class<? extends KeyValueEvent>> eventTypesToPublish = Collections\n\t\t\t.emptySet();\n\n\t/**\n\t * Create new {@link KeyValueTemplate} using the given {@link KeyValueAdapter} with a default\n\t * {@link KeyValueMappingContext}.\n\t *\n\t * @param adapter must not be {@literal null}.\n\t */\n\tpublic KeyValueTemplate(KeyValueAdapter adapter) {\n\t\tthis(adapter, new KeyValueMappingContext<>());\n\t}\n\n\t/**\n\t * Create new {@link KeyValueTemplate} using the given {@link KeyValueAdapter} and {@link MappingContext}.\n\t *\n\t * @param adapter must not be {@literal null}.\n\t * @param mappingContext must not be {@literal null}.\n\t */\n\tpublic KeyValueTemplate(KeyValueAdapter adapter,\n\t\t\tMappingContext<? extends KeyValuePersistentEntity<?, ?>, ? extends KeyValuePersistentProperty<?>> mappingContext) {\n\t\tthis(adapter, mappingContext, DefaultIdentifierGenerator.INSTANCE);\n\t}\n\n\t/**\n\t * Create new {@link KeyValueTemplate} using the given {@link KeyValueAdapter} and {@link MappingContext}.\n\t *\n\t * @param adapter must not be {@literal null}.\n\t * @param mappingContext must not be {@literal null}.\n\t * @param identifierGenerator must not be {@literal null}.\n\t * @since 2.4\n\t */\n\tpublic KeyValueTemplate(KeyValueAdapter adapter,\n\t\t\tMappingContext<? extends KeyValuePersistentEntity<?, ?>, ? extends KeyValuePersistentProperty<?>> mappingContext,\n\t\t\tIdentifierGenerator identifierGenerator) {\n\n\t\tAssert.notNull(adapter, \"Adapter must not be null\");\n\t\tAssert.notNull(mappingContext, \"MappingContext must not be null\");\n\t\tAssert.notNull(identifierGenerator, \"IdentifierGenerator must not be null\");\n\n\t\tthis.adapter = adapter;\n\t\tthis.mappingContext = mappingContext;\n\t\tthis.identifierGenerator = identifierGenerator;\n\t}\n\n\t/**\n\t * Set the {@link PersistenceExceptionTranslator} used for converting {@link RuntimeException}.\n\t *\n\t * @param exceptionTranslator must not be {@literal null}.\n\t */\n\tpublic void setExceptionTranslator(PersistenceExceptionTranslator exceptionTranslator) {\n\n\t\tAssert.notNull(exceptionTranslator, \"ExceptionTranslator must not be null\");\n\t\tthis.exceptionTranslator = exceptionTranslator;\n\t}\n\n\t/**\n\t * Define the event types to publish via {@link ApplicationEventPublisher}.\n\t *\n\t * @param eventTypesToPublish use {@literal null} or {@link Collections#emptySet()} to stop publishing.\n\t */\n\t@SuppressWarnings(\"rawtypes\")\n\tpublic void setEventTypesToPublish(Set<Class<? extends KeyValueEvent>> eventTypesToPublish) {\n\n\t\tif (CollectionUtils.isEmpty(eventTypesToPublish)) {\n\t\t\tthis.publishEvents = false;\n\t\t} else {\n\t\t\tthis.publishEvents = true;\n\t\t\tthis.eventTypesToPublish = Collections.unmodifiableSet(eventTypesToPublish);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {\n\t\tthis.eventPublisher = applicationEventPublisher;\n\t}\n\n\t@Override\n\tpublic <T> T insert(T objectToInsert) {\n\n\t\tKeyValuePersistentEntity<?, ?> entity = getKeyValuePersistentEntity(objectToInsert);\n\n\t\tGeneratingIdAccessor generatingIdAccessor = new GeneratingIdAccessor(entity.getPropertyAccessor(objectToInsert),\n\t\t\t\tentity.getRequiredIdProperty(), identifierGenerator);\n\t\tObject id = generatingIdAccessor.getOrGenerateIdentifier();\n\n\t\treturn insert(id, objectToInsert);\n\t}\n\n\t@Override\n\tpublic <T> T insert(Object id, T objectToInsert) {\n\n\t\tAssert.notNull(id, \"Id for object to be inserted must not be null\");\n\t\tAssert.notNull(objectToInsert, \"Object to be inserted must not be null\");\n\n\t\tString keyspace = resolveKeySpace(objectToInsert.getClass());\n\n\t\tpotentiallyPublishEvent(KeyValueEvent.beforeInsert(id, keyspace, objectToInsert.getClass(), objectToInsert));\n\n\t\texecute((KeyValueCallback<Void>) adapter -> {\n\n\t\t\tif (adapter.contains(id, keyspace)) {\n\t\t\t\tthrow new DuplicateKeyException(\n\t\t\t\t\t\tString.format(\"Cannot insert existing object with id %s; Please use update\", id));\n\t\t\t}\n\n\t\t\tadapter.put(id, objectToInsert, keyspace);\n\t\t\treturn null;\n\t\t});\n\n\t\tpotentiallyPublishEvent(KeyValueEvent.afterInsert(id, keyspace, objectToInsert.getClass(), objectToInsert));\n\n\t\treturn objectToInsert;\n\t}\n\n\t@Override\n\tpublic <T> T update(T objectToUpdate) {\n\n\t\tKeyValuePersistentEntity<?, ?> entity = getKeyValuePersistentEntity(objectToUpdate);\n\n\t\tif (!entity.hasIdProperty()) {\n\t\t\tthrow new InvalidDataAccessApiUsageException(\n\t\t\t\t\tString.format(\"Cannot determine id for type %s\", ClassUtils.getUserClass(objectToUpdate)));\n\t\t}\n\n\t\treturn update(entity.getIdentifierAccessor(objectToUpdate).getRequiredIdentifier(), objectToUpdate);\n\t}\n\n\t@Override\n\tpublic <T> T update(Object id, T objectToUpdate) {\n\n\t\tAssert.notNull(id, \"Id for object to be inserted must not be null\");\n\t\tAssert.notNull(objectToUpdate, \"Object to be updated must not be null\");\n\n\t\tString keyspace = resolveKeySpace(objectToUpdate.getClass());\n\n\t\tpotentiallyPublishEvent(KeyValueEvent.beforeUpdate(id, keyspace, objectToUpdate.getClass(), objectToUpdate));\n\n\t\tObject existing = execute(adapter -> adapter.put(id, objectToUpdate, keyspace));\n\n\t\tpotentiallyPublishEvent(\n\t\t\t\tKeyValueEvent.afterUpdate(id, keyspace, objectToUpdate.getClass(), objectToUpdate, existing));\n\n\t\treturn objectToUpdate;\n\t}\n\n\t@Override\n\tpublic <T> Iterable<T> findAll(Class<T> type) {\n\n\t\tAssert.notNull(type, \"Type to fetch must not be null\");\n\n\t\treturn executeRequired(adapter -> {\n\n\t\t\tString keyspace = resolveKeySpace(type);\n\t\t\tIterable<?> values = adapter.getAllOf(keyspace, type);\n\n\t\t\tArrayList<T> filtered = new ArrayList<>();\n\t\t\tfor (Object candidate : values) {\n\t\t\t\tif (typeCheck(type, candidate)) {\n\t\t\t\t\tfiltered.add(type.cast(candidate));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn filtered;\n\t\t});\n\t}\n\n\t@Override\n\tpublic <T> Optional<T> findById(Object id, Class<T> type) {\n\n\t\tAssert.notNull(id, \"Id for object to be found must not be null\");\n\t\tAssert.notNull(type, \"Type to fetch must not be null\");\n\n\t\tString keyspace = resolveKeySpace(type);\n\n\t\tpotentiallyPublishEvent(KeyValueEvent.beforeGet(id, keyspace, type));\n\n\t\tT result = execute(adapter -> {\n\n\t\t\tObject value = adapter.get(id, keyspace, type);\n\n\t\t\tif (value == null || typeCheck(type, value)) {\n\t\t\t\treturn type.cast(value);\n\t\t\t}\n\n\t\t\treturn null;\n\t\t});\n\n\t\tpotentiallyPublishEvent(KeyValueEvent.afterGet(id, keyspace, type, result));\n\n\t\treturn Optional.ofNullable(result);\n\t}\n\n\t@Override\n\tpublic void delete(Class<?> type) {\n\n\t\tAssert.notNull(type, \"Type to delete must not be null\");\n\n\t\tString keyspace = resolveKeySpace(type);\n\t\tpotentiallyPublishEvent(KeyValueEvent.beforeDropKeySpace(keyspace, type));\n\n\t\texecute((KeyValueCallback<Void>) adapter -> {\n\n\t\t\tadapter.deleteAllOf(keyspace);\n\t\t\treturn null;\n\t\t});\n\n\t\tpotentiallyPublishEvent(KeyValueEvent.afterDropKeySpace(keyspace, type));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic <T> @Nullable T delete(T objectToDelete) {\n\n\t\tClass<T> type = (Class<T>) ClassUtils.getUserClass(objectToDelete);\n\t\tKeyValuePersistentEntity<?, ?> entity = getKeyValuePersistentEntity(objectToDelete);\n\n\t\treturn delete(entity.getIdentifierAccessor(objectToDelete).getRequiredIdentifier(), type);\n\t}\n\n\t@Override\n\tpublic <T> @Nullable T delete(Object id, Class<T> type) {\n\n\t\tAssert.notNull(id, \"Id for object to be deleted must not be null\");\n\t\tAssert.notNull(type, \"Type to delete must not be null\");\n\n\t\tString keyspace = resolveKeySpace(type);\n\n\t\tpotentiallyPublishEvent(KeyValueEvent.beforeDelete(id, keyspace, type));\n\n\t\tT result = execute(adapter -> adapter.delete(id, keyspace, type));\n\n\t\tpotentiallyPublishEvent(KeyValueEvent.afterDelete(id, keyspace, type, result));\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic long count(Class<?> type) {\n\n\t\tAssert.notNull(type, \"Type for count must not be null\");\n\t\tString keyspace = resolveKeySpace(type);\n\t\treturn adapter.count(keyspace);\n\t}\n\n\t@Override\n\tpublic <T> @Nullable T execute(KeyValueCallback<T> action) {\n\n\t\tAssert.notNull(action, \"KeyValueCallback must not be null\");\n\n\t\ttry {\n\t\t\treturn action.doInKeyValue(this.adapter);\n\t\t} catch (RuntimeException e) {\n\t\t\tthrow resolveExceptionIfPossible(e);\n\t\t}\n\t}\n\n\t/**\n\t * Execute {@link KeyValueCallback} and require a non-{@literal null} return value.\n\t *\n\t * @param action\n\t * @param <T>\n\t * @return\n\t */\n\tprotected <T> T executeRequired(KeyValueCallback<T> action) {\n\n\t\tT result = execute(action);\n\n\t\tif (result != null) {\n\t\t\treturn result;\n\t\t}\n\n\t\tthrow new IllegalStateException(String.format(\"KeyValueCallback %s returned null value\", action));\n\t}\n\n\t@Override\n\tpublic <T> Iterable<T> find(KeyValueQuery<?> query, Class<T> type) {\n\n\t\treturn executeRequired((KeyValueCallback<Iterable<T>>) adapter -> {\n\n\t\t\tIterable<?> result = adapter.find(query, resolveKeySpace(type), type);\n\n\t\t\tList<T> filtered = new ArrayList<>();\n\n\t\t\tfor (Object candidate : result) {\n\t\t\t\tif (typeCheck(type, candidate)) {\n\t\t\t\t\tfiltered.add(type.cast(candidate));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn filtered;\n\t\t});\n\t}\n\n\t@SuppressWarnings(\"rawtypes\")\n\t@Override\n\tpublic <T> Iterable<T> findAll(Sort sort, Class<T> type) {\n\t\treturn find(new KeyValueQuery(sort), type);\n\t}\n\n\t@SuppressWarnings(\"rawtypes\")\n\t@Override\n\tpublic <T> Iterable<T> findInRange(long offset, int rows, Class<T> type) {\n\t\treturn find(new KeyValueQuery().skip(offset).limit(rows), type);\n\t}\n\n\t@SuppressWarnings(\"rawtypes\")\n\t@Override\n\tpublic <T> Iterable<T> findInRange(long offset, int rows, Sort sort, Class<T> type) {\n\t\treturn find(new KeyValueQuery(sort).skip(offset).limit(rows), type);\n\t}\n\n\t@Override\n\tpublic long count(KeyValueQuery<?> query, Class<?> type) {\n\t\treturn executeRequired(adapter -> adapter.count(query, resolveKeySpace(type)));\n\t}\n\n\t@Override\n\tpublic boolean exists(KeyValueQuery<?> query, Class<?> type) {\n\t\treturn executeRequired(adapter -> adapter.exists(query, resolveKeySpace(type)));\n\t}\n\n\t@Override\n\tpublic MappingContext<?, ?> getMappingContext() {\n\t\treturn this.mappingContext;\n\t}\n\n\t@Override\n\tpublic KeyValueAdapter getKeyValueAdapter() {\n\t\treturn adapter;\n\t}\n\n\t@Override\n\tpublic void destroy() throws Exception {\n\t\tthis.adapter.clear();\n\t}\n\n\tprivate KeyValuePersistentEntity<?, ?> getKeyValuePersistentEntity(Object objectToInsert) {\n\t\treturn this.mappingContext.getRequiredPersistentEntity(ClassUtils.getUserClass(objectToInsert));\n\t}\n\n\n\tprivate  String resolveKeySpace(Class<?> type) {\n\n\t\tString keyspace = this.mappingContext.getRequiredPersistentEntity(type).getKeySpace();\n\t\tAssert.notNull(keyspace, \"Keyspace must not be null\");\n\t\treturn keyspace;\n\t}\n\n\tprivate RuntimeException resolveExceptionIfPossible(RuntimeException e) {\n\n\t\tDataAccessException translatedException = exceptionTranslator.translateExceptionIfPossible(e);\n\t\treturn translatedException != null ? translatedException : e;\n\t}\n\n\t@SuppressWarnings(\"rawtypes\")\n\tprivate void potentiallyPublishEvent(KeyValueEvent event) {\n\n\t\tif (eventPublisher == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (publishEvents && (eventTypesToPublish.isEmpty() || eventTypesToPublish.contains(event.getClass()))) {\n\t\t\teventPublisher.publishEvent(event);\n\t\t}\n\t}\n\n\tprivate static boolean typeCheck(Class<?> requiredType, @Nullable Object candidate) {\n\t\treturn candidate == null || ClassUtils.isAssignable(requiredType, candidate.getClass());\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/PathSortAccessor.java",
    "content": "/*\n * Copyright 2024-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport java.util.Comparator;\nimport java.util.Optional;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.data.domain.Sort.Direction;\nimport org.springframework.data.domain.Sort.NullHandling;\nimport org.springframework.data.domain.Sort.Order;\nimport org.springframework.data.keyvalue.core.query.KeyValueQuery;\n\n/**\n * @author Christoph Strobl\n * @since 3.1.10\n */\npublic class PathSortAccessor implements SortAccessor<Comparator<?>> {\n\n\t@Override\n\tpublic @Nullable Comparator<?> resolve(KeyValueQuery<?> query) {\n\n\t\tif (query.getSort().isUnsorted()) {\n\t\t\treturn null;\n\t\t}\n\n\t\tOptional<Comparator<?>> comparator = Optional.empty();\n\t\tfor (Order order : query.getSort()) {\n\n\t\t\tPropertyPathComparator<Object> pathSort = new PropertyPathComparator<>(order.getProperty());\n\n\t\t\tif (Direction.DESC.equals(order.getDirection())) {\n\n\t\t\t\tpathSort.desc();\n\n\t\t\t\tif (!NullHandling.NATIVE.equals(order.getNullHandling())) {\n\t\t\t\t\tpathSort = NullHandling.NULLS_FIRST.equals(order.getNullHandling()) ? pathSort.nullsFirst()\n\t\t\t\t\t\t\t: pathSort.nullsLast();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!comparator.isPresent()) {\n\t\t\t\tcomparator = Optional.of(pathSort);\n\t\t\t} else {\n\n\t\t\t\tPropertyPathComparator<Object> pathSortToUse = pathSort;\n\t\t\t\tcomparator = comparator.map(it -> it.thenComparing(pathSortToUse));\n\t\t\t}\n\t\t}\n\n\t\treturn comparator.orElseThrow(\n\t\t\t\t() -> new IllegalStateException(\"No sort definitions have been added to this CompoundComparator to compare\"));\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/PredicateQueryEngine.java",
    "content": "/*\n * Copyright 2024-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport java.util.Collection;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.data.keyvalue.core.query.KeyValueQuery;\nimport org.springframework.lang.Contract;\n\n/**\n * {@link QueryEngine} implementation specific for executing {@link Predicate} based {@link KeyValueQuery} against\n * {@link KeyValueAdapter}.\n *\n * @author Christoph Strobl\n * @since 3.3\n */\npublic class PredicateQueryEngine extends QueryEngine<KeyValueAdapter, Predicate<?>, Comparator<?>> {\n\n\tpublic static final PredicateQueryEngine INSTANCE = new PredicateQueryEngine();\n\n\t/**\n\t * Creates a new {@link PredicateQueryEngine}.\n\t */\n\tpublic PredicateQueryEngine() {\n\t\tthis(new PathSortAccessor());\n\t}\n\n\t/**\n\t * Creates a new query engine using provided {@link SortAccessor accessor} for sorting results.\n\t */\n\tpublic PredicateQueryEngine(SortAccessor<Comparator<?>> sortAccessor) {\n\t\tsuper(new CriteriaAccessor<>() {\n\n\t\t\t@Override\n\t\t\tpublic @Nullable Predicate<?> resolve(KeyValueQuery<?> query) {\n\t\t\t\treturn (Predicate<?>) query.getCriteria();\n\t\t\t}\n\t\t}, sortAccessor);\n\t}\n\n\t@Override\n\tpublic Collection<?> execute(@Nullable Predicate<?> criteria, @Nullable Comparator<?> sort, long offset, int rows,\n\t\t\tString keyspace) {\n\t\treturn sortAndFilterMatchingRange(getRequiredAdapter().getAllOf(keyspace), criteria, sort, offset, rows);\n\t}\n\n\t@Override\n\tpublic long count(@Nullable Predicate<?> criteria, String keyspace) {\n\t\treturn filterMatchingRange(IterableConverter.toList(getRequiredAdapter().getAllOf(keyspace)), criteria, -1, -1)\n\t\t\t\t.size();\n\t}\n\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\tprivate List<?> sortAndFilterMatchingRange(Iterable<?> source, @Nullable Predicate<?> criteria,\n\t\t\t@Nullable Comparator sort, long offset, int rows) {\n\n\t\tList<?> tmp = IterableConverter.toList(source);\n\t\tif (sort != null) {\n\t\t\ttmp.sort(sort);\n\t\t}\n\n\t\treturn filterMatchingRange(tmp, criteria, offset, rows);\n\t}\n\n\t@Contract(\"!null, _, _, _ -> !null\")\n\tprivate static <S> List<S> filterMatchingRange(List<S> source, @Nullable Predicate<?> criteria, long offset, int rows) {\n\n\t\tStream<S> stream = source.stream();\n\n\t\tif (criteria != null) {\n\t\t\tstream = stream.filter((Predicate<? super S>) criteria);\n\t\t}\n\t\tif (offset > 0) {\n\t\t\tstream = stream.skip(offset);\n\t\t}\n\t\tif (rows > 0) {\n\t\t\tstream = stream.limit(rows);\n\t\t}\n\n\t\treturn stream.collect(Collectors.toList());\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/PropertyPathComparator.java",
    "content": "/*\n * Copyright 2024-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.data.core.PropertyPath;\nimport org.springframework.lang.Contract;\n\n/**\n * {@link Comparator} implementation to compare objects based on a {@link PropertyPath}. This comparator obtains the\n * value at {@link PropertyPath} from the {@link #compare(Object, Object) given comparison objects} and then performs\n * the comparison.\n *\n * @author Christoph Strobl\n * @author Mark Paluch\n * @since 3.1.10\n */\npublic class PropertyPathComparator<T> implements Comparator<T> {\n\n\tprivate static final Comparator<?> NULLS_FIRST = Comparator.nullsFirst(Comparator.naturalOrder());\n\tprivate static final Comparator<?> NULLS_LAST = Comparator.nullsLast(Comparator.naturalOrder());\n\n\tprivate final String path;\n\n\tprivate boolean asc = true;\n\tprivate boolean nullsFirst = true;\n\n\tprivate final Map<Class<?>, PropertyPath> pathCache = new HashMap<>(2);\n\n\tpublic PropertyPathComparator(String path) {\n\t\tthis.path = path;\n\t}\n\n\t@Override\n\tpublic int compare(@Nullable T o1, @Nullable T o2) {\n\n\t\tif (o1 == null && o2 == null) {\n\t\t\treturn 0;\n\t\t}\n\t\tif (o1 == null) {\n\t\t\treturn nullsFirst ? 1 : -1;\n\t\t}\n\t\tif (o2 == null) {\n\t\t\treturn nullsFirst ? 1 : -1;\n\t\t}\n\n\t\tPropertyPath propertyPath = pathCache.computeIfAbsent(o1.getClass(), it -> PropertyPath.from(path, it));\n\t\tObject value1 = getCompareValue(o1, propertyPath);\n\t\tObject value2 = getCompareValue(o2, propertyPath);\n\n\t\treturn getComparator().compare(value1, value2) * (asc ? 1 : -1);\n\t}\n\n\tprotected <S> @Nullable Object getCompareValue(S object, PropertyPath propertyPath) {\n\t\treturn new SimplePropertyPathAccessor<>(object).getValue(propertyPath);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate Comparator<@Nullable Object> getComparator() {\n\t\treturn (Comparator<Object>) (nullsFirst ? NULLS_FIRST : NULLS_LAST);\n\t}\n\n\t/**\n\t * Sort {@literal ascending}.\n\t *\n\t * @return\n\t */\n\t@Contract(\"-> this\")\n\tpublic PropertyPathComparator<@Nullable T> asc() {\n\t\tthis.asc = true;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sort {@literal descending}.\n\t *\n\t * @return\n\t */\n\t@Contract(\"-> this\")\n\tpublic PropertyPathComparator<@Nullable T> desc() {\n\t\tthis.asc = false;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sort {@literal null} values first.\n\t *\n\t * @return\n\t */\n\t@Contract(\"-> this\")\n\tpublic PropertyPathComparator<@Nullable T> nullsFirst() {\n\t\tthis.nullsFirst = true;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sort {@literal null} values last.\n\t *\n\t * @return\n\t */\n\t@Contract(\"-> this\")\n\tpublic PropertyPathComparator<@Nullable T> nullsLast() {\n\t\tthis.nullsFirst = false;\n\t\treturn this;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/QueryEngine.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport java.util.Collection;\nimport java.util.Optional;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.data.keyvalue.core.query.KeyValueQuery;\n\n/**\n * Base implementation for accessing and executing {@link KeyValueQuery} against a {@link KeyValueAdapter}.\n *\n * @author Christoph Strobl\n * @author Mark Paluch\n * @param <ADAPTER>\n * @param <CRITERIA>\n * @param <SORT>\n */\npublic abstract class QueryEngine<ADAPTER extends KeyValueAdapter, CRITERIA, SORT> {\n\n\tprivate final Optional<CriteriaAccessor<CRITERIA>> criteriaAccessor;\n\tprivate final Optional<SortAccessor<SORT>> sortAccessor;\n\n\tprivate @Nullable ADAPTER adapter;\n\n\tpublic QueryEngine(@Nullable CriteriaAccessor<CRITERIA> criteriaAccessor, @Nullable SortAccessor<SORT> sortAccessor) {\n\n\t\tthis.criteriaAccessor = Optional.ofNullable(criteriaAccessor);\n\t\tthis.sortAccessor = Optional.ofNullable(sortAccessor);\n\t}\n\n\t/**\n\t * Extract query attributes and delegate to concrete execution.\n\t *\n\t * @param query\n\t * @param keyspace\n\t * @return\n\t */\n\tpublic Collection<?> execute(KeyValueQuery<?> query, String keyspace) {\n\n\t\tCRITERIA criteria = this.criteriaAccessor.map(it -> it.resolve(query)).orElse(null);\n\t\tSORT sort = this.sortAccessor.map(it -> it.resolve(query)).orElse(null);\n\n\t\treturn execute(criteria, sort, query.getOffset(), query.getRows(), keyspace);\n\t}\n\n\t/**\n\t * Extract query attributes and delegate to concrete execution.\n\t *\n\t * @param query\n\t * @param keyspace\n\t * @return\n\t */\n\tpublic <T> Collection<T> execute(KeyValueQuery<?> query, String keyspace, Class<T> type) {\n\n\t\tCRITERIA criteria = this.criteriaAccessor.map(it -> it.resolve(query)).orElse(null);\n\t\tSORT sort = this.sortAccessor.map(it -> it.resolve(query)).orElse(null);\n\n\t\treturn execute(criteria, sort, query.getOffset(), query.getRows(), keyspace, type);\n\t}\n\n\t/**\n\t * Extract query attributes and delegate to concrete execution.\n\t *\n\t * @param query\n\t * @param keyspace\n\t * @return\n\t */\n\tpublic long count(KeyValueQuery<?> query, String keyspace) {\n\n\t\tCRITERIA criteria = this.criteriaAccessor.map(it -> it.resolve(query)).orElse(null);\n\t\treturn count(criteria, keyspace);\n\t}\n\n\t/**\n\t * @param criteria\n\t * @param sort\n\t * @param offset\n\t * @param rows\n\t * @param keyspace\n\t * @return\n\t */\n\tpublic abstract Collection<?> execute(@Nullable CRITERIA criteria, @Nullable SORT sort, long offset, int rows,\n\t\t\tString keyspace);\n\n\t/**\n\t * @param criteria\n\t * @param sort\n\t * @param offset\n\t * @param rows\n\t * @param keyspace\n\t * @param type\n\t * @return\n\t * @since 1.1\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T> Collection<T> execute(@Nullable CRITERIA criteria, @Nullable SORT sort, long offset, int rows,\n\t\t\tString keyspace, Class<T> type) {\n\t\treturn (Collection<T>) execute(criteria, sort, offset, rows, keyspace);\n\t}\n\n\t/**\n\t * @param criteria\n\t * @param keyspace\n\t * @return\n\t */\n\tpublic abstract long count(@Nullable CRITERIA criteria, String keyspace);\n\n\t/**\n\t * Get the {@link KeyValueAdapter} used.\n\t *\n\t * @return\n\t */\n\tprotected @Nullable ADAPTER getAdapter() {\n\t\treturn this.adapter;\n\t}\n\n\t/**\n\t * Get the required {@link KeyValueAdapter} used or throw {@link IllegalStateException} if the adapter is not set.\n\t *\n\t * @return the required {@link KeyValueAdapter}.\n\t * @throws IllegalStateException if the adapter is not set.\n\t */\n\tprotected ADAPTER getRequiredAdapter() {\n\n\t\tADAPTER adapter = getAdapter();\n\n\t\tif (adapter != null) {\n\t\t\treturn adapter;\n\t\t}\n\n\t\tthrow new IllegalStateException(\"Required KeyValueAdapter is not set\");\n\t}\n\n\t/**\n\t * @param adapter\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void registerAdapter(KeyValueAdapter adapter) {\n\n\t\tif (this.adapter == null) {\n\t\t\tthis.adapter = (ADAPTER) adapter;\n\t\t} else {\n\t\t\tthrow new IllegalArgumentException(\"Cannot register more than one adapter for this QueryEngine\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/QueryEngineFactory.java",
    "content": "/*\n * Copyright 2024-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\n/**\n * Interface for {@code QueryEngineFactory} implementations that provide a {@link QueryEngine} object as part of the\n * configuration.\n * <p>\n * The factory is used during configuration to supply the query engine to be used. When configured, a\n * {@code QueryEngineFactory} can be instantiated by accepting a {@link SortAccessor} in its constructor. Otherwise,\n * implementations are expected to declare a no-args constructor.\n *\n * @author Mark Paluch\n * @since 3.3.1\n */\npublic interface QueryEngineFactory {\n\n\t/**\n\t * Factory method for creating a {@link QueryEngine}.\n\t *\n\t * @return the query engine.\n\t */\n\tQueryEngine<?, ?, ?> create();\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/SimplePropertyPathAccessor.java",
    "content": "/*\n * Copyright 2024-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.beans.BeanWrapper;\nimport org.springframework.data.core.PropertyPath;\nimport org.springframework.data.util.DirectFieldAccessFallbackBeanWrapper;\n\n/**\n * @author Christoph Strobl\n * @since 3.1.10\n */\npublic class SimplePropertyPathAccessor<T> {\n\n\tprivate final Object root;\n\n\tpublic SimplePropertyPathAccessor(Object source) {\n\t\tthis.root = source;\n\t}\n\n\tpublic @Nullable Object getValue(PropertyPath path) {\n\n\t\tObject currentValue = root;\n\t\tfor (PropertyPath current : path) {\n\t\t\tcurrentValue = wrap(currentValue).getPropertyValue(current.getSegment());\n\t\t\tif (currentValue == null) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn currentValue;\n\t}\n\n\tBeanWrapper wrap(Object o) {\n\t\treturn new DirectFieldAccessFallbackBeanWrapper(o);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/SortAccessor.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.data.domain.Sort;\nimport org.springframework.data.keyvalue.core.query.KeyValueQuery;\n\n/**\n * Resolves the {@link Sort} object from given {@link KeyValueQuery} and potentially converts it into a store specific\n * representation that can be used by the {@link QueryEngine} implementation.\n *\n * @author Christoph Strobl\n * @author Mark Paluch\n * @param <T>\n */\npublic interface SortAccessor<T> {\n\n\t/**\n\t * Reads {@link KeyValueQuery#getSort()} of given {@link KeyValueQuery} and applies required transformation to match\n\t * the desired type.\n\t *\n\t * @param query must not be {@literal null}.\n\t * @return {@literal null} in case {@link Sort} has not been defined on {@link KeyValueQuery}.\n\t */\n\t@Nullable\n\tT resolve(KeyValueQuery<?> query);\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/SpelCriteria.java",
    "content": "/*\n * Copyright 2016-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.spel.standard.SpelExpression;\nimport org.springframework.expression.spel.support.SimpleEvaluationContext;\nimport org.springframework.util.Assert;\n\n/**\n * {@link SpelCriteria} allows to pass on a {@link SpelExpression} and {@link EvaluationContext} to the actual query\n * processor. This decouples the {@link SpelExpression} from the context it is used in.\n *\n * @author Christoph Strobl\n * @author Oliver Gierke\n */\npublic class SpelCriteria {\n\n\tprivate final SpelExpression expression;\n\tprivate final EvaluationContext context;\n\n\t/**\n\t * Creates a new {@link SpelCriteria} for the given {@link SpelExpression}.\n\t *\n\t * @param expression must not be {@literal null}.\n\t */\n\tpublic SpelCriteria(SpelExpression expression) {\n\t\tthis(expression, SimpleEvaluationContext.forReadOnlyDataBinding().withInstanceMethods().build());\n\t}\n\n\t/**\n\t * Creates new {@link SpelCriteria}.\n\t *\n\t * @param expression must not be {@literal null}.\n\t * @param context must not be {@literal null}.\n\t */\n\tpublic SpelCriteria(SpelExpression expression, EvaluationContext context) {\n\n\t\tAssert.notNull(expression, \"SpEL expression must not be null\");\n\t\tAssert.notNull(context, \"EvaluationContext must not be null\");\n\n\t\tthis.expression = expression;\n\t\tthis.context = context;\n\t}\n\n\t/**\n\t * @return will never be {@literal null}.\n\t */\n\tpublic EvaluationContext getContext() {\n\t\treturn context;\n\t}\n\n\t/**\n\t * @return will never be {@literal null}.\n\t */\n\tpublic SpelExpression getExpression() {\n\t\treturn expression;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/SpelCriteriaAccessor.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.data.keyvalue.core.query.KeyValueQuery;\nimport org.springframework.expression.spel.standard.SpelExpression;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.util.Assert;\n\n/**\n * {@link CriteriaAccessor} implementation capable of {@link SpelExpression}s.\n *\n * @author Christoph Strobl\n * @author Oliver Gierke\n */\nclass SpelCriteriaAccessor implements CriteriaAccessor<SpelCriteria> {\n\n\tprivate final SpelExpressionParser parser;\n\n\t/**\n\t * Creates a new {@link SpelCriteriaAccessor} using the given {@link SpelExpressionParser}.\n\t *\n\t * @param parser must not be {@literal null}.\n\t */\n\tpublic SpelCriteriaAccessor(SpelExpressionParser parser) {\n\n\t\tAssert.notNull(parser, \"SpelExpressionParser must not be null\");\n\n\t\tthis.parser = parser;\n\t}\n\n\t@Override\n\tpublic @Nullable SpelCriteria resolve(KeyValueQuery<?> query) {\n\n\t\tif (query.getCriteria() == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (query.getCriteria() instanceof SpelExpression) {\n\t\t\treturn new SpelCriteria((SpelExpression) query.getCriteria());\n\t\t}\n\n\t\tif (query.getCriteria() instanceof String) {\n\t\t\treturn new SpelCriteria(parser.parseRaw((String) query.getCriteria()));\n\t\t}\n\n\t\tif (query.getCriteria() instanceof SpelCriteria) {\n\t\t\treturn (SpelCriteria) query.getCriteria();\n\t\t}\n\n\t\tthrow new IllegalArgumentException(\"Cannot create SpelCriteria for \" + query.getCriteria());\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/SpelPropertyComparator.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport java.util.Comparator;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.expression.spel.standard.SpelExpression;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.SimpleEvaluationContext;\nimport org.springframework.lang.Contract;\nimport org.springframework.util.Assert;\n\n/**\n * {@link Comparator} implementation using {@link SpelExpression}.\n *\n * @author Christoph Strobl\n * @author Oliver Gierke\n * @author Mark Paluch\n * @param <T>\n */\npublic class SpelPropertyComparator<T> implements Comparator<T> {\n\n\tprivate static final Comparator<?> NULLS_FIRST = Comparator.nullsFirst(Comparator.naturalOrder());\n\tprivate static final Comparator<?> NULLS_LAST = Comparator.nullsLast(Comparator.naturalOrder());\n\n\tprivate final String path;\n\tprivate final SpelExpressionParser parser;\n\n\tprivate boolean asc = true;\n\tprivate boolean nullsFirst = true;\n\tprivate @Nullable SpelExpression expression;\n\n\t/**\n\t * Create new {@link SpelPropertyComparator} for the given property path an {@link SpelExpressionParser}.\n\t *\n\t * @param path must not be {@literal null} or empty.\n\t * @param parser must not be {@literal null}.\n\t */\n\tpublic SpelPropertyComparator(String path, SpelExpressionParser parser) {\n\n\t\tAssert.hasText(path, \"Path must not be null or empty\");\n\t\tAssert.notNull(parser, \"SpelExpressionParser must not be null\");\n\n\t\tthis.path = path;\n\t\tthis.parser = parser;\n\t}\n\n\t/**\n\t * Sort {@literal ascending}.\n\t *\n\t * @return\n\t */\n\t@Contract(\"-> this\")\n\tpublic SpelPropertyComparator<@Nullable T> asc() {\n\t\tthis.asc = true;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sort {@literal descending}.\n\t *\n\t * @return\n\t */\n\t@Contract(\"-> this\")\n\tpublic SpelPropertyComparator<@Nullable T> desc() {\n\t\tthis.asc = false;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sort {@literal null} values first.\n\t *\n\t * @return\n\t */\n\t@Contract(\"-> this\")\n\tpublic SpelPropertyComparator<@Nullable T> nullsFirst() {\n\t\tthis.nullsFirst = true;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sort {@literal null} values last.\n\t *\n\t * @return\n\t */\n\t@Contract(\"-> this\")\n\tpublic SpelPropertyComparator<@Nullable T> nullsLast() {\n\t\tthis.nullsFirst = false;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Parse values to {@link SpelExpression}\n\t *\n\t * @return\n\t */\n\tprotected SpelExpression getExpression() {\n\n\t\tif (this.expression == null) {\n\t\t\tthis.expression = parser.parseRaw(buildExpressionForPath());\n\t\t}\n\n\t\treturn this.expression;\n\t}\n\n\t/**\n\t * Create the expression raw value.\n\t *\n\t * @return\n\t */\n\tprotected String buildExpressionForPath() {\n\n\t\treturn String.format(\"#comparator.compare(#arg1?.%s,#arg2?.%s)\", path.replace(\".\", \"?.\"),\n\t\t\t\tpath.replace(\".\", \"?.\"));\n\t}\n\n\t@Override\n\tpublic int compare(@Nullable T arg1, @Nullable T arg2) {\n\n\t\tSpelExpression expressionToUse = getExpression();\n\n\t\tSimpleEvaluationContext ctx = SimpleEvaluationContext.forReadOnlyDataBinding().withInstanceMethods().build();\n\t\tctx.setVariable(\"comparator\", nullsFirst ? NULLS_FIRST : NULLS_LAST);\n\t\tctx.setVariable(\"arg1\", arg1);\n\t\tctx.setVariable(\"arg2\", arg2);\n\n\t\texpressionToUse.setEvaluationContext(ctx);\n\n\t\tInteger value = expressionToUse.getValue(Integer.class);\n\t\treturn (value != null ? value : 0) * (asc ? 1 : -1);\n\t}\n\n\t/**\n\t * Get dot path to property.\n\t *\n\t * @return\n\t */\n\tpublic String getPath() {\n\t\treturn path;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/SpelQueryEngine.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport java.util.Collection;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.data.keyvalue.core.query.KeyValueQuery;\nimport org.springframework.expression.spel.SpelEvaluationException;\nimport org.springframework.expression.spel.standard.SpelExpression;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\n\n/**\n * {@link QueryEngine} implementation specific for executing {@link SpelExpression} based {@link KeyValueQuery} against\n * {@link KeyValueAdapter}.\n *\n * @author Christoph Strobl\n * @author Oliver Gierke\n * @author Mark Paluch\n */\npublic class SpelQueryEngine extends QueryEngine<KeyValueAdapter, SpelCriteria, Comparator<?>> {\n\n\tprivate static final SpelExpressionParser PARSER = new SpelExpressionParser();\n\n\t/**\n\t * Creates a new {@link SpelQueryEngine}.\n\t */\n\tpublic SpelQueryEngine() {\n\t\tthis(new SpelSortAccessor(PARSER));\n\t}\n\n\t/**\n\t * Creates a new query engine using provided {@link SortAccessor accessor} for sorting results.\n\t *\n\t * @since 3.1.10\n\t */\n\tpublic SpelQueryEngine(SortAccessor<Comparator<?>> sortAccessor) {\n\t\tsuper(new SpelCriteriaAccessor(PARSER), sortAccessor);\n\t}\n\n\t@Override\n\tpublic Collection<?> execute(@Nullable SpelCriteria criteria, @Nullable Comparator<?> sort, long offset, int rows,\n\t\t\tString keyspace) {\n\t\treturn sortAndFilterMatchingRange(getRequiredAdapter().getAllOf(keyspace), criteria, sort, offset, rows);\n\t}\n\n\t@Override\n\tpublic long count(@Nullable SpelCriteria criteria, String keyspace) {\n\t\treturn filterMatchingRange(IterableConverter.toList(getRequiredAdapter().getAllOf(keyspace)), criteria, -1, -1)\n\t\t\t\t.size();\n\t}\n\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\tprivate List<?> sortAndFilterMatchingRange(Iterable<?> source, @Nullable SpelCriteria criteria,\n\t\t\t@Nullable Comparator sort, long offset, int rows) {\n\n\t\tList<?> tmp = IterableConverter.toList(source);\n\t\tif (sort != null) {\n\t\t\ttmp.sort(sort);\n\t\t}\n\n\t\treturn filterMatchingRange(tmp, criteria, offset, rows);\n\t}\n\n\tprivate static <S> List<S> filterMatchingRange(List<S> source, @Nullable SpelCriteria criteria, long offset,\n\t\t\tint rows) {\n\n\t\tStream<S> stream = source.stream();\n\n\t\tif (criteria != null) {\n\t\t\tstream = stream.filter(it -> evaluateExpression(criteria, it));\n\t\t}\n\t\tif (offset > 0) {\n\t\t\tstream = stream.skip(offset);\n\t\t}\n\t\tif (rows > 0) {\n\t\t\tstream = stream.limit(rows);\n\t\t}\n\n\t\treturn stream.collect(Collectors.toList());\n\t}\n\n\t@SuppressWarnings(\"NullAway\")\n\tprivate static boolean evaluateExpression(SpelCriteria criteria, Object candidate) {\n\n\t\ttry {\n\t\t\treturn criteria.getExpression().getValue(criteria.getContext(), candidate, Boolean.class);\n\t\t} catch (SpelEvaluationException e) {\n\t\t\tcriteria.getContext().setVariable(\"it\", candidate);\n\t\t\treturn criteria.getExpression().getValue(criteria.getContext()) == null ? false\n\t\t\t\t\t: criteria.getExpression().getValue(criteria.getContext(), Boolean.class);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/SpelSortAccessor.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport java.util.Comparator;\nimport java.util.Optional;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.data.domain.Sort.Direction;\nimport org.springframework.data.domain.Sort.NullHandling;\nimport org.springframework.data.domain.Sort.Order;\nimport org.springframework.data.keyvalue.core.query.KeyValueQuery;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.util.Assert;\n\n/**\n * {@link SortAccessor} implementation capable of creating {@link SpelPropertyComparator}.\n *\n * @author Christoph Strobl\n * @author Oliver Gierke\n * @author Mark Paluch\n */\npublic class SpelSortAccessor implements SortAccessor<Comparator<?>> {\n\n\tprivate final SpelExpressionParser parser;\n\n\t/**\n\t * Creates a new {@link SpelSortAccessor} given {@link SpelExpressionParser}.\n\t *\n\t * @param parser must not be {@literal null}.\n\t */\n\tpublic SpelSortAccessor(SpelExpressionParser parser) {\n\n\t\tAssert.notNull(parser, \"SpelExpressionParser must not be null\");\n\t\tthis.parser = parser;\n\t}\n\n\t@Override\n\tpublic @Nullable Comparator<?> resolve(KeyValueQuery<?> query) {\n\n\t\tif (query.getSort().isUnsorted()) {\n\t\t\treturn null;\n\t\t}\n\n\t\tOptional<Comparator<?>> comparator = Optional.empty();\n\t\tfor (Order order : query.getSort()) {\n\n\t\t\tSpelPropertyComparator<Object> spelSort = new SpelPropertyComparator<>(order.getProperty(), parser);\n\n\t\t\tif (Direction.DESC.equals(order.getDirection())) {\n\n\t\t\t\tspelSort.desc();\n\n\t\t\t\tif (!NullHandling.NATIVE.equals(order.getNullHandling())) {\n\t\t\t\t\tspelSort = NullHandling.NULLS_FIRST.equals(order.getNullHandling()) ? spelSort.nullsFirst()\n\t\t\t\t\t\t\t: spelSort.nullsLast();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!comparator.isPresent()) {\n\t\t\t\tcomparator = Optional.of(spelSort);\n\t\t\t} else {\n\n\t\t\t\tSpelPropertyComparator<Object> spelSortToUse = spelSort;\n\t\t\t\tcomparator = comparator.map(it -> it.thenComparing(spelSortToUse));\n\t\t\t}\n\t\t}\n\n\t\treturn comparator.orElseThrow(\n\t\t\t\t() -> new IllegalStateException(\"No sort definitions have been added to this CompoundComparator to compare\"));\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/UncategorizedKeyValueException.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport org.springframework.dao.UncategorizedDataAccessException;\n\n/**\n * Normal superclass when we can't distinguish anything more specific than \"something went wrong with the underlying\n * resource\".\n *\n * @author Christoph Strobl\n * @author Mark Paluch\n */\npublic class UncategorizedKeyValueException extends UncategorizedDataAccessException {\n\n\tprivate static final long serialVersionUID = -8087116071859122297L;\n\n\t/**\n\t * Creates a new {@link UncategorizedKeyValueException}.\n\t *\n\t * @param msg the detail message.\n\t * @param cause the root cause (usually from using a underlying data access API).\n\t */\n\tpublic UncategorizedKeyValueException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/event/KeyValueEvent.java",
    "content": "/*\n * Copyright 2015-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core.event;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.context.ApplicationEvent;\n\n/**\n * {@link KeyValueEvent} gets published for operations executed by eg.\n * {@link org.springframework.data.keyvalue.core.KeyValueTemplate}.\n *\n * Use the {@link KeyValueEvent.KeyBasedEvent#getType()} to determine which event has been emitted.\n *\n * @author Christoph Strobl\n * @author Thomas Darimont\n * @author Mark Paluch\n * @param <T>\n */\npublic class KeyValueEvent<T> extends ApplicationEvent {\n\n\tprivate static final long serialVersionUID = -7128527253428193044L;\n\n\tprivate final String keyspace;\n\n\tprotected KeyValueEvent(Object source, String keyspace) {\n\n\t\tsuper(source);\n\t\tthis.keyspace = keyspace;\n\t}\n\n\t/**\n\t * @return affected keyspace. Never {@literal null}.\n\t */\n\tpublic String getKeyspace() {\n\t\treturn keyspace;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"KeyValueEvent [keyspace=\" + keyspace + \", source=\" + getSource() + \"]\";\n\t}\n\n\t/**\n\t * Create new {@link BeforeGetEvent}.\n\t *\n\t * @param id\n\t * @param keyspace\n\t * @param type\n\t * @return\n\t */\n\tpublic static <T> BeforeGetEvent<T> beforeGet(Object id, String keyspace, Class<T> type) {\n\t\treturn new BeforeGetEvent<>(id, keyspace, type);\n\t}\n\n\t/**\n\t * Create new {@link AfterGetEvent}.\n\t *\n\t * @param id\n\t * @param keyspace\n\t * @param type\n\t * @param value\n\t * @return\n\t */\n\tpublic static <T> AfterGetEvent<T> afterGet(Object id, String keyspace, Class<T> type, @Nullable T value) {\n\t\treturn new AfterGetEvent<>(id, keyspace, type, value);\n\t}\n\n\t/**\n\t * Create new {@link BeforeInsertEvent}.\n\t *\n\t * @param id\n\t * @param keyspace\n\t * @param type\n\t * @param value\n\t * @return\n\t */\n\tpublic static <T> BeforeInsertEvent<T> beforeInsert(Object id, String keyspace, Class<? extends T> type, T value) {\n\t\treturn new BeforeInsertEvent<>(id, keyspace, type, value);\n\t}\n\n\t/**\n\t * Create new {@link AfterInsertEvent}.\n\t *\n\t * @param id\n\t * @param keyspace\n\t * @param type\n\t * @param value\n\t * @return\n\t */\n\tpublic static <T> AfterInsertEvent<T> afterInsert(Object id, String keyspace, Class<? extends T> type, T value) {\n\t\treturn new AfterInsertEvent<>(id, keyspace, type, value);\n\t}\n\n\t/**\n\t * Create new {@link BeforeUpdateEvent}.\n\t *\n\t * @param id\n\t * @param keyspace\n\t * @param type\n\t * @param value\n\t * @return\n\t */\n\tpublic static <T> BeforeUpdateEvent<T> beforeUpdate(Object id, String keyspace, Class<? extends T> type, T value) {\n\t\treturn new BeforeUpdateEvent<>(id, keyspace, type, value);\n\t}\n\n\t/**\n\t * Create new {@link AfterUpdateEvent}.\n\t *\n\t * @param id\n\t * @param keyspace\n\t * @param type\n\t * @param actualValue\n\t * @param previousValue\n\t * @return\n\t */\n\tpublic static <T> AfterUpdateEvent<T> afterUpdate(Object id, String keyspace, Class<? extends T> type, T actualValue,\n\t\t\t@Nullable Object previousValue) {\n\t\treturn new AfterUpdateEvent<>(id, keyspace, type, actualValue, previousValue);\n\t}\n\n\t/**\n\t * Create new {@link BeforeDropKeySpaceEvent}.\n\t *\n\t * @param keyspace\n\t * @param type\n\t * @return\n\t */\n\tpublic static <T> BeforeDropKeySpaceEvent<T> beforeDropKeySpace(String keyspace, Class<? extends T> type) {\n\t\treturn new BeforeDropKeySpaceEvent<>(keyspace, type);\n\t}\n\n\t/**\n\t * Create new {@link AfterDropKeySpaceEvent}.\n\t *\n\t * @param keyspace\n\t * @param type\n\t * @return\n\t */\n\tpublic static <T> AfterDropKeySpaceEvent<T> afterDropKeySpace(String keyspace, Class<? extends T> type) {\n\t\treturn new AfterDropKeySpaceEvent<>(keyspace, type);\n\t}\n\n\t/**\n\t * Create new {@link BeforeDeleteEvent}.\n\t *\n\t * @param id\n\t * @param keyspace\n\t * @param type\n\t * @return\n\t */\n\tpublic static <T> BeforeDeleteEvent<T> beforeDelete(Object id, String keyspace, Class<? extends T> type) {\n\t\treturn new BeforeDeleteEvent<>(id, keyspace, type);\n\t}\n\n\t/**\n\t * Create new {@link AfterDeleteEvent}.\n\t *\n\t * @param id\n\t * @param keyspace\n\t * @param type\n\t * @param value\n\t * @return\n\t */\n\tpublic static <T> AfterDeleteEvent<T> afterDelete(Object id, String keyspace, Class<? extends T> type,\n\t\t\t@Nullable T value) {\n\t\treturn new AfterDeleteEvent<>(id, keyspace, type, value);\n\t}\n\n\t/**\n\t * @author Christoph Strobl\n\t * @param <T>\n\t */\n\t@SuppressWarnings(\"serial\")\n\tabstract static class KeyBasedEvent<T> extends KeyValueEvent<T> {\n\n\t\tprivate Object key;\n\t\tprivate Class<? extends T> type;\n\n\t\tKeyBasedEvent(Object key, String keyspace, Class<? extends T> type) {\n\n\t\t\tsuper(type, keyspace);\n\t\t\tthis.key = key;\n\t\t\tthis.type = type;\n\t\t}\n\n\t\tpublic Object getKey() {\n\t\t\treturn key;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getSource() {\n\t\t\treturn getKey();\n\t\t}\n\n\t\t/**\n\t\t * Get the type of the element the {@link KeyValueEvent} refers to.\n\t\t *\n\t\t * @return\n\t\t */\n\t\tpublic Class<? extends T> getType() {\n\t\t\treturn type;\n\t\t}\n\t}\n\n\t/**\n\t * @author Christoph Strobl\n\t * @param <T>\n\t */\n\t@SuppressWarnings(\"serial\")\n\tabstract static class KeyBasedEventWithPayload<T> extends KeyBasedEvent<T> {\n\n\t\tprivate final @Nullable T payload;\n\n\t\tKeyBasedEventWithPayload(Object key, String keyspace, Class<? extends T> type, @Nullable T payload) {\n\t\t\tsuper(key, keyspace, type);\n\t\t\tthis.payload = payload;\n\t\t}\n\n\t\t/**\n\t\t * Get the value of the element the {@link KeyValueEvent} refers to. Can be {@literal null}.\n\t\t *\n\t\t * @return\n\t\t */\n\t\tpublic @Nullable T getPayload() {\n\t\t\treturn payload;\n\t\t}\n\t}\n\n\t/**\n\t * {@link KeyValueEvent} raised before loading an object by its {@literal key}.\n\t *\n\t * @author Christoph Strobl\n\t * @param <T>\n\t */\n\t@SuppressWarnings(\"serial\")\n\tpublic static class BeforeGetEvent<T> extends KeyBasedEvent<T> {\n\n\t\tprotected BeforeGetEvent(Object key, String keyspace, Class<T> type) {\n\t\t\tsuper(key, keyspace, type);\n\t\t}\n\n\t}\n\n\t/**\n\t * {@link KeyValueEvent} after loading an object by its {@literal key}.\n\t *\n\t * @author Christoph Strobl\n\t * @param <T>\n\t */\n\t@SuppressWarnings(\"serial\")\n\tpublic static class AfterGetEvent<T> extends KeyBasedEventWithPayload<T> {\n\n\t\tprotected AfterGetEvent(Object key, String keyspace, Class<T> type, @Nullable T payload) {\n\t\t\tsuper(key, keyspace, type, payload);\n\t\t}\n\n\t}\n\n\t/**\n\t * {@link KeyValueEvent} before inserting an object by with a given {@literal key}.\n\t *\n\t * @author Christoph Strobl\n\t * @param <T>\n\t */\n\t@SuppressWarnings(\"serial\")\n\tpublic static class BeforeInsertEvent<T> extends KeyBasedEventWithPayload<T> {\n\n\t\tpublic BeforeInsertEvent(Object key, String keyspace, Class<? extends T> type, @Nullable T payload) {\n\t\t\tsuper(key, keyspace, type, payload);\n\n\t\t}\n\t}\n\n\t/**\n\t * {@link KeyValueEvent} after inserting an object by with a given {@literal key}.\n\t *\n\t * @author Christoph Strobl\n\t * @param <T>\n\t */\n\t@SuppressWarnings(\"serial\")\n\tpublic static class AfterInsertEvent<T> extends KeyBasedEventWithPayload<T> {\n\n\t\tpublic AfterInsertEvent(Object key, String keyspace, Class<? extends T> type, @Nullable T payload) {\n\t\t\tsuper(key, keyspace, type, payload);\n\t\t}\n\t}\n\n\t/**\n\t * {@link KeyValueEvent} before updating an object by with a given {@literal key}.\n\t *\n\t * @author Christoph Strobl\n\t * @param <T>\n\t */\n\t@SuppressWarnings(\"serial\")\n\tpublic static class BeforeUpdateEvent<T> extends KeyBasedEventWithPayload<T> {\n\n\t\tpublic BeforeUpdateEvent(Object key, String keyspace, Class<? extends T> type, @Nullable T payload) {\n\t\t\tsuper(key, keyspace, type, payload);\n\t\t}\n\t}\n\n\t/**\n\t * {@link KeyValueEvent} after updating an object by with a given {@literal key}.\n\t *\n\t * @author Christoph Strobl\n\t * @param <T>\n\t */\n\t@SuppressWarnings(\"serial\")\n\tpublic static class AfterUpdateEvent<T> extends KeyBasedEventWithPayload<T> {\n\n\t\tprivate final @Nullable Object existing;\n\n\t\tpublic AfterUpdateEvent(Object key, String keyspace, Class<? extends T> type, T payload,\n\t\t\t\t@Nullable Object existing) {\n\t\t\tsuper(key, keyspace, type, payload);\n\t\t\tthis.existing = existing;\n\t\t}\n\n\t\t/**\n\t\t * Get the value before update. Can be {@literal null}.\n\t\t *\n\t\t * @return\n\t\t */\n\t\tpublic @Nullable Object before() {\n\t\t\treturn existing;\n\t\t}\n\n\t\t/**\n\t\t * Get the current value.\n\t\t *\n\t\t * @return can be {@literal null}.\n\t\t */\n\t\tpublic @Nullable T after() {\n\t\t\treturn getPayload();\n\t\t}\n\t}\n\n\t/**\n\t * {@link KeyValueEvent} before removing an object by with a given {@literal key}.\n\t *\n\t * @author Christoph Strobl\n\t * @param <T>\n\t */\n\t@SuppressWarnings(\"serial\")\n\tpublic static class BeforeDeleteEvent<T> extends KeyBasedEvent<T> {\n\n\t\tpublic BeforeDeleteEvent(Object key, String keyspace, Class<? extends T> type) {\n\t\t\tsuper(key, keyspace, type);\n\t\t}\n\t}\n\n\t/**\n\t * {@link KeyValueEvent} after removing an object by with a given {@literal key}.\n\t *\n\t * @author Christoph Strobl\n\t * @param <T>\n\t */\n\t@SuppressWarnings(\"serial\")\n\tpublic static class AfterDeleteEvent<T> extends KeyBasedEventWithPayload<T> {\n\n\t\tpublic AfterDeleteEvent(Object key, String keyspace, Class<? extends T> type, @Nullable T payload) {\n\t\t\tsuper(key, keyspace, type, payload);\n\t\t}\n\t}\n\n\t/**\n\t * {@link KeyValueEvent} before removing all elements in a given {@literal keyspace}.\n\t *\n\t * @author Christoph Strobl\n\t * @param <T>\n\t */\n\t@SuppressWarnings(\"serial\")\n\tpublic static class BeforeDropKeySpaceEvent<T> extends KeyValueEvent<T> {\n\n\t\tpublic BeforeDropKeySpaceEvent(String keyspace, Class<? extends T> type) {\n\t\t\tsuper(type, keyspace);\n\t\t}\n\n\t\t@Override\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tpublic Class<T> getSource() {\n\t\t\treturn (Class<T>) super.getSource();\n\t\t}\n\n\t}\n\n\t/**\n\t * {@link KeyValueEvent} after removing all elements in a given {@literal keyspace}.\n\t *\n\t * @author Christoph Strobl\n\t * @param <T>\n\t */\n\t@SuppressWarnings(\"serial\")\n\tpublic static class AfterDropKeySpaceEvent<T> extends KeyValueEvent<T> {\n\n\t\tpublic AfterDropKeySpaceEvent(String keyspace, Class<? extends T> type) {\n\t\t\tsuper(type, keyspace);\n\t\t}\n\n\t\t@Override\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tpublic Class<T> getSource() {\n\t\t\treturn (Class<T>) super.getSource();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/event/package-info.java",
    "content": "/**\n * Support classes for key-value events, like standard persistence lifecycle events.\n */\n@org.jspecify.annotations.NullMarked\npackage org.springframework.data.keyvalue.core.event;\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/mapping/AnnotationBasedKeySpaceResolver.java",
    "content": "/*\n * Copyright 2015-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core.mapping;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.core.annotation.MergedAnnotation;\nimport org.springframework.core.annotation.MergedAnnotations;\nimport org.springframework.data.annotation.Persistent;\nimport org.springframework.data.keyvalue.annotation.KeySpace;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\n\n/**\n * {@link AnnotationBasedKeySpaceResolver} looks up {@link Persistent} and checks for presence of either meta or direct\n * usage of {@link KeySpace}. If non found it will default the keyspace to {@link Class#getName()}.\n *\n * @author Christoph Strobl\n * @author Oliver Gierke\n * @author Mark Paluch\n */\npublic enum AnnotationBasedKeySpaceResolver implements KeySpaceResolver {\n\n\tINSTANCE;\n\n\t@Override\n\tpublic @Nullable String resolveKeySpace(Class<?> type) {\n\n\t\tAssert.notNull(type, \"Type for keyspace for null\");\n\n\t\tClass<?> userClass = ClassUtils.getUserClass(type);\n\t\tObject keySpace = getKeySpace(userClass);\n\n\t\treturn keySpace != null ? keySpace.toString() : null;\n\t}\n\n\n\tprivate static @Nullable Object getKeySpace(Class<?> type) {\n\n\t\tMergedAnnotation<KeySpace> annotation = MergedAnnotations\n\t\t\t\t.from(type, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY).get(KeySpace.class);\n\n\t\tif (annotation.isPresent()) {\n\t\t\treturn annotation.getValue(\"value\").orElse(null);\n\t\t}\n\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/mapping/BasicKeyValuePersistentEntity.java",
    "content": "/*\n * Copyright 2015-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core.mapping;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.data.core.TypeInformation;\nimport org.springframework.data.expression.ValueExpression;\nimport org.springframework.data.expression.ValueExpressionParser;\nimport org.springframework.data.mapping.model.BasicPersistentEntity;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.common.LiteralExpression;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.util.ObjectUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * {@link KeyValuePersistentEntity} implementation that adds specific meta-data such as the {@literal keySpace}.\n *\n * @author Christoph Strobl\n * @author Oliver Gierke\n * @author Mark Paluch\n * @param <T>\n */\npublic class BasicKeyValuePersistentEntity<T, P extends KeyValuePersistentProperty<P>>\n\t\textends BasicPersistentEntity<T, P> implements KeyValuePersistentEntity<T, P> {\n\n\tprivate static final ValueExpressionParser PARSER = ValueExpressionParser.create(SpelExpressionParser::new);\n\n\tprivate final @Nullable ValueExpression keyspaceExpression;\n\tprivate final String keyspace;\n\n\t/**\n\t * @param information must not be {@literal null}.\n\t * @since 3.1\n\t */\n\tpublic BasicKeyValuePersistentEntity(TypeInformation<T> information) {\n\t\tthis(information, (String) null);\n\t}\n\n\t/**\n\t * @param information must not be {@literal null}.\n\t * @param keySpaceResolver can be {@literal null}.\n\t */\n\tpublic BasicKeyValuePersistentEntity(TypeInformation<T> information, @Nullable KeySpaceResolver keySpaceResolver) {\n\t\tthis(information, keySpaceResolver != null ? keySpaceResolver.resolveKeySpace(information.getType()) : null);\n\t}\n\n\tprivate BasicKeyValuePersistentEntity(TypeInformation<T> information, @Nullable String keyspace) {\n\n\t\tsuper(information);\n\n\t\tif (StringUtils.hasText(keyspace)) {\n\n\t\t\tthis.keyspace = keyspace;\n\t\t\tthis.keyspaceExpression = null;\n\t\t} else {\n\n\t\t\tClass<T> type = information.getType();\n\t\t\tString detectedKeyspace = AnnotationBasedKeySpaceResolver.INSTANCE.resolveKeySpace(type);\n\n\t\t\tif (StringUtils.hasText(detectedKeyspace)) {\n\n\t\t\t\tthis.keyspace = detectedKeyspace;\n\t\t\t\tthis.keyspaceExpression = detectExpression(detectedKeyspace);\n\t\t\t} else {\n\n\t\t\t\tthis.keyspace = ClassNameKeySpaceResolver.INSTANCE.resolveKeySpace(type);\n\t\t\t\tthis.keyspaceExpression = null;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Returns a SpEL {@link Expression} if the given {@link String} is actually an expression that does not evaluate to a\n\t * {@link LiteralExpression} (indicating that no subsequent evaluation is necessary).\n\t *\n\t * @param potentialExpression must not be {@literal null}\n\t * @return the parsed {@link Expression} or {@literal null}.\n\t */\n\n\tprivate static @Nullable ValueExpression detectExpression(String potentialExpression) {\n\n\t\tValueExpression expression = PARSER.parse(potentialExpression);\n\t\treturn expression.isLiteral() ? null : expression;\n\t}\n\n\t@Override\n\tpublic String getKeySpace() {\n\t\treturn keyspaceExpression == null //\n\t\t\t\t? keyspace //\n\t\t\t\t: ObjectUtils.nullSafeToString(keyspaceExpression.evaluate(getValueEvaluationContext(null)));\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/mapping/ClassNameKeySpaceResolver.java",
    "content": "/*\n * Copyright 2015-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core.mapping;\n\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Most trivial implementation of {@link KeySpaceResolver} returning the {@link Class#getName()}.\n *\n * @author Christoph Strobl\n * @author Oliver Gierke\n */\npublic enum ClassNameKeySpaceResolver implements KeySpaceResolver {\n\n\tINSTANCE;\n\n\t@Override\n\tpublic String resolveKeySpace(Class<?> type) {\n\n\t\tAssert.notNull(type, \"Type must not be null\");\n\t\treturn ClassUtils.getUserClass(type).getName();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/mapping/KeySpaceResolver.java",
    "content": "/*\n * Copyright 2015-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core.mapping;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * {@link KeySpaceResolver} determines the {@literal keyspace} a given type is assigned to. A keyspace in this context\n * is a specific region/collection/grouping of elements sharing a common keyrange.\n *\n * @author Christoph Strobl\n * @author Mark Paluch\n */\npublic interface KeySpaceResolver {\n\n\t/**\n\t * Determine the {@literal keySpace} to use for a given type.\n\t *\n\t * @param type must not be {@literal null}.\n\t * @return\n\t */\n\t@Nullable\n\tString resolveKeySpace(Class<?> type);\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/mapping/KeyValuePersistentEntity.java",
    "content": "/*\n * Copyright 2015-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core.mapping;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.data.mapping.PersistentEntity;\nimport org.springframework.data.mapping.model.MutablePersistentEntity;\n\n/**\n * KeyValue-specific extension of {@link PersistentEntity} declaring a {@literal keySpace}.\n *\n * @author Christoph Strobl\n * @author Mark Paluch\n * @param <T>\n */\npublic interface KeyValuePersistentEntity<T, P extends KeyValuePersistentProperty<P>>\n\t\textends MutablePersistentEntity<T, P> {\n\n\t/**\n\t * Get the {@literal keySpace} a given entity assigns to.\n\t *\n\t * @return can be {@literal null}.\n\t */\n\t@Nullable\n\tString getKeySpace();\n\n\t/**\n\t * Returns the required {@literal keySpace} or throws an {@link IllegalStateException} if the {@literal keySpace} is\n\t * not defined.\n\t *\n\t * @return the {@literal keySpace} property of this {@link PersistentEntity}.\n\t * @throws IllegalStateException if the {@literal keySpace} is not defined.\n\t * @since 4.0\n\t */\n\tdefault String getRequiredKeySpace() {\n\n\t\tString keySpace = getKeySpace();\n\n\t\tif (keySpace != null) {\n\t\t\treturn keySpace;\n\t\t}\n\n\t\tthrow new IllegalStateException(String.format(\"Required keySpace not defined for %s\", getType()));\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/mapping/KeyValuePersistentProperty.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core.mapping;\n\nimport org.springframework.data.mapping.Association;\nimport org.springframework.data.mapping.PersistentEntity;\nimport org.springframework.data.mapping.PersistentProperty;\nimport org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;\nimport org.springframework.data.mapping.model.Property;\nimport org.springframework.data.mapping.model.SimpleTypeHolder;\n\n/**\n * Most trivial implementation of {@link PersistentProperty}.\n *\n * @author Christoph Strobl\n * @author Mark Paluch\n */\npublic class KeyValuePersistentProperty<P extends KeyValuePersistentProperty<P>>\n\t\textends AnnotationBasedPersistentProperty<P> {\n\n\tpublic KeyValuePersistentProperty(Property property, PersistentEntity<?, P> owner,\n\t\t\tSimpleTypeHolder simpleTypeHolder) {\n\t\tsuper(property, owner, simpleTypeHolder);\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tprotected Association<P> createAssociation() {\n\t\treturn new Association<>((P) this, null);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/mapping/PrefixKeyspaceResolver.java",
    "content": "/*\n * Copyright 2022-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core.mapping;\n\nimport org.springframework.util.Assert;\n\n/**\n * {@link KeySpaceResolver} prefixing the {@literal keyspace} with a static prefix after determining the keyspace from a\n * delegate {@link KeySpaceResolver}.\n *\n * @author Mark Paluch\n * @since 3.1\n */\npublic class PrefixKeyspaceResolver implements KeySpaceResolver {\n\n\tprivate final String prefix;\n\tprivate final KeySpaceResolver delegate;\n\n\tpublic PrefixKeyspaceResolver(String prefix, KeySpaceResolver delegate) {\n\n\t\tAssert.notNull(prefix, \"Prefix must not be null\");\n\t\tAssert.notNull(delegate, \"Delegate KeySpaceResolver must not be null\");\n\n\t\tthis.prefix = prefix;\n\t\tthis.delegate = delegate;\n\t}\n\n\t@Override\n\tpublic String resolveKeySpace(Class<?> type) {\n\t\treturn prefix + delegate.resolveKeySpace(type);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/mapping/context/KeyValueMappingContext.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core.mapping.context;\n\nimport java.util.Collections;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.data.core.TypeInformation;\nimport org.springframework.data.keyvalue.core.mapping.BasicKeyValuePersistentEntity;\nimport org.springframework.data.keyvalue.core.mapping.KeySpaceResolver;\nimport org.springframework.data.keyvalue.core.mapping.KeyValuePersistentEntity;\nimport org.springframework.data.keyvalue.core.mapping.KeyValuePersistentProperty;\nimport org.springframework.data.mapping.context.AbstractMappingContext;\nimport org.springframework.data.mapping.context.MappingContext;\nimport org.springframework.data.mapping.model.Property;\nimport org.springframework.data.mapping.model.SimpleTypeHolder;\n\n/**\n * Default implementation of a {@link MappingContext} using {@link KeyValuePersistentEntity} and\n * {@link KeyValuePersistentProperty} as primary abstractions.\n *\n * @author Christoph Strobl\n * @author Oliver Gierke\n * @author Mark Paluch\n */\npublic class KeyValueMappingContext<E extends KeyValuePersistentEntity<?, P>, P extends KeyValuePersistentProperty<P>>\n\t\textends AbstractMappingContext<E, P> {\n\n\tprivate @Nullable KeySpaceResolver keySpaceResolver;\n\n\tpublic KeyValueMappingContext() {\n\t\tsetSimpleTypeHolder(new KeyValueSimpleTypeHolder());\n\t}\n\n\t/**\n\t * Configures the {@link KeySpaceResolver} to be used if not explicit key space is annotated to the domain type.\n\t *\n\t * @param fallbackKeySpaceResolver can be {@literal null}.\n\t * @deprecated since 3.1, use {@link KeySpaceResolver} instead.\n\t */\n\t@Deprecated(since = \"3.1\")\n\tpublic void setFallbackKeySpaceResolver(KeySpaceResolver fallbackKeySpaceResolver) {\n\t\tsetKeySpaceResolver(fallbackKeySpaceResolver);\n\t}\n\n\t/**\n\t * Configures the {@link KeySpaceResolver} to be used. Configuring a {@link KeySpaceResolver} disables SpEL evaluation\n\t * abilities.\n\t *\n\t * @param keySpaceResolver can be {@literal null}.\n\t * @since 3.1\n\t */\n\tpublic void setKeySpaceResolver(KeySpaceResolver keySpaceResolver) {\n\t\tthis.keySpaceResolver = keySpaceResolver;\n\t}\n\n\t/**\n\t * @return the current {@link KeySpaceResolver}. Can be {@literal null}.\n\t * @since 3.1\n\t */\n\tpublic @Nullable KeySpaceResolver getKeySpaceResolver() {\n\t\treturn keySpaceResolver;\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tprotected <T> E createPersistentEntity(TypeInformation<T> typeInformation) {\n\t\treturn (E) new BasicKeyValuePersistentEntity<T, P>(typeInformation, getKeySpaceResolver());\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tprotected P createPersistentProperty(Property property, E owner, SimpleTypeHolder simpleTypeHolder) {\n\t\treturn (P) new KeyValuePersistentProperty<>(property, owner, simpleTypeHolder);\n\t}\n\n\t/**\n\t * @since 2.5.1\n\t */\n\tprivate static class KeyValueSimpleTypeHolder extends SimpleTypeHolder {\n\n\t\tpublic KeyValueSimpleTypeHolder() {\n\t\t\tsuper(Collections.emptySet(), true);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isSimpleType(Class<?> type) {\n\n\t\t\tif (type.getName().startsWith(\"java.\") || type.getName().startsWith(\"javax.\")) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\treturn super.isSimpleType(type);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/mapping/context/package-info.java",
    "content": "/**\n * Infrastructure for the Key-Value mapping context.\n */\n@org.jspecify.annotations.NullMarked\npackage org.springframework.data.keyvalue.core.mapping.context;\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/mapping/package-info.java",
    "content": "/**\n * Infrastructure for the Key-Value mapping subsystem and keyspace resolution.\n */\n@org.jspecify.annotations.NullMarked\npackage org.springframework.data.keyvalue.core.mapping;\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/package-info.java",
    "content": "/**\n * Core key/value implementation.\n */\n@org.jspecify.annotations.NullMarked\npackage org.springframework.data.keyvalue.core;\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/query/KeyValueQuery.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core.query;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.data.domain.Sort;\nimport org.springframework.lang.Contract;\nimport org.springframework.util.Assert;\n\n/**\n * @author Christoph Strobl\n * @author Mark Paluch\n * @author Marcel Overdijk\n * @param <T> Criteria type\n */\npublic class KeyValueQuery<T> {\n\n\tprivate Sort sort = Sort.unsorted();\n\tprivate long offset = -1;\n\tprivate int rows = -1;\n\tprivate final @Nullable T criteria;\n\n\t/**\n\t * Creates new instance of {@link KeyValueQuery}.\n\t */\n\tpublic KeyValueQuery() {\n\t\tthis((T) null);\n\t}\n\n\t/**\n\t * Creates new instance of {@link KeyValueQuery} with given criteria.\n\t *\n\t * @param criteria can be {@literal null}.\n\t */\n\tpublic KeyValueQuery(@Nullable T criteria) {\n\t\tthis.criteria = criteria;\n\t}\n\n\t/**\n\t * Creates new instance of {@link KeyValueQuery} with given criteria and {@link Sort}.\n\t *\n\t * @param criteria can be {@literal null}.\n\t * @param sort must not be {@literal null}.\n\t * @since 2.4\n\t */\n\tpublic KeyValueQuery(@Nullable T criteria, Sort sort) {\n\t\tthis.criteria = criteria;\n\t\tsetSort(sort);\n\t}\n\n\t/**\n\t * Creates new instance of {@link KeyValueQuery} with given {@link Sort}.\n\t *\n\t * @param sort must not be {@literal null}.\n\t */\n\tpublic KeyValueQuery(Sort sort) {\n\t\tthis();\n\t\tsetSort(sort);\n\t}\n\n\t/**\n\t * Get the criteria object.\n\t *\n\t * @return\n\t * @since 2.0\n\t */\n\tpublic @Nullable T getCriteria() {\n\t\treturn criteria;\n\t}\n\n\t/**\n\t * Get {@link Sort}.\n\t *\n\t * @return\n\t */\n\tpublic Sort getSort() {\n\t\treturn sort;\n\t}\n\n\t/**\n\t * Number of elements to skip.\n\t *\n\t * @return negative value if not set.\n\t */\n\tpublic long getOffset() {\n\t\treturn this.offset;\n\t}\n\n\t/**\n\t * Number of elements to read.\n\t *\n\t * @return negative value if not set.\n\t */\n\tpublic int getRows() {\n\t\treturn this.rows;\n\t}\n\n\t/**\n\t * Set the number of elements to skip.\n\t *\n\t * @param offset use negative value for none.\n\t */\n\tpublic void setOffset(long offset) {\n\t\tthis.offset = offset;\n\t}\n\n\t/**\n\t * Set the number of elements to read.\n\t *\n\t * @param rows use negative value for all.\n\t */\n\tpublic void setRows(int rows) {\n\t\tthis.rows = rows;\n\t}\n\n\t/**\n\t * Set {@link Sort} to be applied.\n\t *\n\t * @param sort\n\t */\n\tpublic void setSort(Sort sort) {\n\n\t\tAssert.notNull(sort, \"Sort must not be null\");\n\n\t\tthis.sort = sort;\n\t}\n\n\t/**\n\t * Add given {@link Sort}.\n\t *\n\t * @param sort must not be {@literal null}.\n\t * @return\n\t */\n\t@Contract(\"_ -> this\")\n\tpublic KeyValueQuery<T> orderBy(Sort sort) {\n\n\t\tAssert.notNull(sort, \"Sort must not be null\");\n\n\t\tif (this.sort.isSorted()) {\n\t\t\tthis.sort = this.sort.and(sort);\n\t\t} else {\n\t\t\tthis.sort = sort;\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * @see KeyValueQuery#setOffset(long)\n\t * @param offset\n\t * @return\n\t */\n\t@Contract(\"_ -> this\")\n\tpublic KeyValueQuery<T> skip(long offset) {\n\n\t\tsetOffset(offset);\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * @see KeyValueQuery#setRows(int)\n\t * @param rows\n\t * @return\n\t */\n\t@Contract(\"_ -> this\")\n\tpublic KeyValueQuery<T> limit(int rows) {\n\t\tsetRows(rows);\n\t\treturn this;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/core/query/package-info.java",
    "content": "/**\n * Key/value specific query and abstractions.\n */\n@org.jspecify.annotations.NullMarked\npackage org.springframework.data.keyvalue.core.query;\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/repository/KeyValueRepository.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.repository;\n\nimport org.springframework.data.repository.ListCrudRepository;\nimport org.springframework.data.repository.ListPagingAndSortingRepository;\n\n/**\n * @author Christoph Strobl\n * @param <T>\n * @param <ID>\n */\npublic interface KeyValueRepository<T, ID> extends ListCrudRepository<T, ID>, ListPagingAndSortingRepository<T, ID> {\n\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/repository/config/KeyValueRepositoryConfigurationExtension.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.repository.config;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.beans.factory.support.AbstractBeanDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.core.annotation.AnnotationAttributes;\nimport org.springframework.core.annotation.MergedAnnotation;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.data.keyvalue.core.mapping.context.KeyValueMappingContext;\nimport org.springframework.data.keyvalue.repository.KeyValueRepository;\nimport org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery;\nimport org.springframework.data.keyvalue.repository.query.SpelQueryCreator;\nimport org.springframework.data.keyvalue.repository.support.KeyValueRepositoryFactoryBean;\nimport org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource;\nimport org.springframework.data.repository.config.RepositoryConfigurationExtension;\nimport org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport;\nimport org.springframework.data.repository.config.RepositoryConfigurationSource;\n\n/**\n * {@link RepositoryConfigurationExtension} for {@link KeyValueRepository}.\n *\n * @author Christoph Strobl\n * @author Oliver Gierke\n * @author Mark Paluch\n */\npublic abstract class KeyValueRepositoryConfigurationExtension extends RepositoryConfigurationExtensionSupport {\n\n\tprotected static final String MAPPING_CONTEXT_BEAN_NAME = \"keyValueMappingContext\";\n\tprotected static final String KEY_VALUE_TEMPLATE_BEAN_REF_ATTRIBUTE = \"keyValueTemplateRef\";\n\n\t@Override\n\tpublic String getRepositoryFactoryBeanClassName() {\n\t\treturn KeyValueRepositoryFactoryBean.class.getName();\n\t}\n\n\t@Override\n\tpublic String getModuleName() {\n\t\treturn \"KeyValue\";\n\t}\n\n\t@Override\n\tprotected String getModulePrefix() {\n\t\treturn getModuleIdentifier();\n\t}\n\n\t@Override\n\tprotected Collection<Class<?>> getIdentifyingTypes() {\n\t\treturn Collections.singleton(KeyValueRepository.class);\n\t}\n\n\t@Override\n\tpublic void postProcess(BeanDefinitionBuilder builder, AnnotationRepositoryConfigurationSource config) {\n\n\t\tAnnotationAttributes attributes = config.getAttributes();\n\n\t\tbuilder.addPropertyReference(\"keyValueOperations\", attributes.getString(KEY_VALUE_TEMPLATE_BEAN_REF_ATTRIBUTE));\n\t\tbuilder.addPropertyValue(\"queryCreator\", getQueryCreatorType(config));\n\t\tbuilder.addPropertyValue(\"queryType\", getQueryType(config));\n\t\tbuilder.addPropertyReference(\"mappingContext\", getMappingContextBeanRef());\n\t}\n\n\t/**\n\t * Detects the query creator type to be used for the factory to set. Will lookup a {@link QueryCreatorType} annotation\n\t * on the {@code @Enable}-annotation or use {@link SpelQueryCreator} if not found.\n\t *\n\t * @param config must not be {@literal null}.\n\t * @return\n\t */\n\tprivate static Class<?> getQueryCreatorType(AnnotationRepositoryConfigurationSource config) {\n\n\t\tAnnotationMetadata amd = (AnnotationMetadata) config.getSource();\n\t\tMergedAnnotation<QueryCreatorType> queryCreator = amd.getAnnotations().get(QueryCreatorType.class);\n\t\tClass<?> queryCreatorType = queryCreator.isPresent() ? queryCreator.getClass(\"value\") : Class.class;\n\n\t\tif (queryCreatorType == Class.class) {\n\t\t\treturn SpelQueryCreator.class;\n\t\t}\n\n\t\treturn queryCreatorType;\n\t}\n\n\t/**\n\t * Detects the query creator type to be used for the factory to set. Will lookup a {@link QueryCreatorType} annotation\n\t * on the {@code @Enable}-annotation or use {@link SpelQueryCreator} if not found.\n\t *\n\t * @param config\n\t * @return\n\t */\n\tprivate static Class<?> getQueryType(AnnotationRepositoryConfigurationSource config) {\n\n\t\tAnnotationMetadata metadata = config.getEnableAnnotationMetadata();\n\n\t\tMap<String, Object> queryCreatorAnnotationAttributes = metadata\n\t\t\t\t.getAnnotationAttributes(QueryCreatorType.class.getName());\n\n\t\tif (queryCreatorAnnotationAttributes == null) {\n\t\t\treturn KeyValuePartTreeQuery.class;\n\t\t}\n\n\t\tAnnotationAttributes queryCreatorAttributes = new AnnotationAttributes(queryCreatorAnnotationAttributes);\n\t\treturn queryCreatorAttributes.getClass(\"repositoryQueryType\");\n\t}\n\n\t@Override\n\tpublic void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConfigurationSource configurationSource) {\n\n\t\tsuper.registerBeansForRoot(registry, configurationSource);\n\n\t\tregisterIfNotAlreadyRegistered(() -> {\n\n\t\t\tRootBeanDefinition mappingContext = new RootBeanDefinition(KeyValueMappingContext.class);\n\t\t\tmappingContext.setSource(configurationSource.getSource());\n\n\t\t\treturn mappingContext;\n\n\t\t}, registry, getMappingContextBeanRef(), configurationSource);\n\n\t\tOptional<String> keyValueTemplateName = configurationSource.getAttribute(KEY_VALUE_TEMPLATE_BEAN_REF_ATTRIBUTE);\n\n\t\t// No custom template reference configured and no matching bean definition found\n\t\tif (keyValueTemplateName.isPresent() && getDefaultKeyValueTemplateRef().equals(keyValueTemplateName.get())\n\t\t\t\t&& !registry.containsBeanDefinition(keyValueTemplateName.get())) {\n\n\t\t\tAbstractBeanDefinition beanDefinition = getDefaultKeyValueTemplateBeanDefinition(configurationSource);\n\n\t\t\tif (beanDefinition != null && configurationSource.getSource() != null) {\n\t\t\t\tregisterIfNotAlreadyRegistered(() -> beanDefinition, registry, keyValueTemplateName.get(),\n\t\t\t\t\t\tconfigurationSource.getSource());\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Get the default {@link RootBeanDefinition} for {@link org.springframework.data.keyvalue.core.KeyValueTemplate}.\n\t *\n\t * @return {@literal null} to explicitly not register a template.\n\t * @see #getDefaultKeyValueTemplateRef()\n\t */\n\tprotected @Nullable AbstractBeanDefinition getDefaultKeyValueTemplateBeanDefinition(\n\t\t\tRepositoryConfigurationSource configurationSource) {\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the {@link org.springframework.data.keyvalue.core.KeyValueTemplate} bean name to potentially register a\n\t * default {@link org.springframework.data.keyvalue.core.KeyValueTemplate} bean if no bean is registered with the\n\t * returned name.\n\t *\n\t * @return the default {@link org.springframework.data.keyvalue.core.KeyValueTemplate} bean name. Never\n\t *         {@literal null}.\n\t * @see #getDefaultKeyValueTemplateBeanDefinition(RepositoryConfigurationSource)\n\t */\n\tprotected abstract String getDefaultKeyValueTemplateRef();\n\n\t/**\n\t * Returns the {@link org.springframework.data.mapping.context.MappingContext} bean name to potentially register a\n\t * default mapping context bean if no bean is registered with the returned name. Defaults to\n\t * {@link MAPPING_CONTEXT_BEAN_NAME}.\n\t *\n\t * @return the {@link org.springframework.data.mapping.context.MappingContext} bean name. Never {@literal null}.\n\t * @since 2.0\n\t */\n\tprotected String getMappingContextBeanRef() {\n\t\treturn MAPPING_CONTEXT_BEAN_NAME;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/repository/config/QueryCreatorType.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.repository.config;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery;\nimport org.springframework.data.repository.query.RepositoryQuery;\nimport org.springframework.data.repository.query.parser.AbstractQueryCreator;\n\n/**\n * Annotation to customize the query creator type to be used for a specific store.\n *\n * @author Oliver Gierke\n * @author Christoph Strobl\n */\n@Documented\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.ANNOTATION_TYPE)\npublic @interface QueryCreatorType {\n\n\tClass<? extends AbstractQueryCreator<?, ?>> value();\n\n\t/**\n\t * The {@link RepositoryQuery} type to be created by the {@link QueryCreatorType#value()}.\n\t *\n\t * @return\n\t * @since 1.1\n\t */\n\tClass<? extends RepositoryQuery> repositoryQueryType() default KeyValuePartTreeQuery.class;\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/repository/config/package-info.java",
    "content": "/**\n * Support infrastructure for the configuration of key/value specific repositories.\n */\n@org.jspecify.annotations.NullMarked\npackage org.springframework.data.keyvalue.repository.config;\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/repository/package-info.java",
    "content": "/**\n * Key/value specific repository implementation.\n */\n@org.jspecify.annotations.NullMarked\npackage org.springframework.data.keyvalue.repository;\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/repository/query/CachingKeyValuePartTreeQuery.java",
    "content": "/*\n * Copyright 2016-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.repository.query;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.data.keyvalue.core.KeyValueOperations;\nimport org.springframework.data.keyvalue.core.query.KeyValueQuery;\nimport org.springframework.data.repository.query.QueryMethod;\nimport org.springframework.data.repository.query.ValueExpressionDelegate;\nimport org.springframework.data.repository.query.parser.AbstractQueryCreator;\nimport org.springframework.data.repository.query.parser.PartTree;\n\n/**\n * {@link KeyValuePartTreeQuery} implementation deriving queries from {@link PartTree} using a predefined\n * {@link AbstractQueryCreator} that caches the once created query.\n *\n * @author Christoph Strobl\n * @author Mark Paluch\n * @since 1.1\n */\npublic class CachingKeyValuePartTreeQuery extends KeyValuePartTreeQuery {\n\n\tprivate @Nullable KeyValueQuery<?> cachedQuery;\n\n\tpublic CachingKeyValuePartTreeQuery(QueryMethod queryMethod,\n\t\t\tValueExpressionDelegate valueExpressionDelegate, KeyValueOperations keyValueOperations,\n\t\t\tClass<? extends AbstractQueryCreator<?, ?>> queryCreator) {\n\t\tsuper(queryMethod, valueExpressionDelegate, keyValueOperations, queryCreator);\n\t}\n\n\tprotected KeyValueQuery<?> prepareQuery(Object[] parameters) {\n\n\t\tif (cachedQuery == null) {\n\t\t\tcachedQuery = super.prepareQuery(parameters);\n\t\t}\n\n\t\treturn prepareQuery(cachedQuery, parameters);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/repository/query/KeyValuePartTreeQuery.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.repository.query;\n\nimport java.lang.reflect.Constructor;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.data.domain.PageImpl;\nimport org.springframework.data.domain.Pageable;\nimport org.springframework.data.domain.Sort;\nimport org.springframework.data.expression.ValueEvaluationContext;\nimport org.springframework.data.expression.ValueEvaluationContextProvider;\nimport org.springframework.data.keyvalue.core.IterableConverter;\nimport org.springframework.data.keyvalue.core.KeyValueOperations;\nimport org.springframework.data.keyvalue.core.SpelCriteria;\nimport org.springframework.data.keyvalue.core.query.KeyValueQuery;\nimport org.springframework.data.repository.query.ParameterAccessor;\nimport org.springframework.data.repository.query.ParametersParameterAccessor;\nimport org.springframework.data.repository.query.QueryMethod;\nimport org.springframework.data.repository.query.RepositoryQuery;\nimport org.springframework.data.repository.query.ResultProcessor;\nimport org.springframework.data.repository.query.ValueExpressionDelegate;\nimport org.springframework.data.repository.query.parser.AbstractQueryCreator;\nimport org.springframework.data.repository.query.parser.PartTree;\nimport org.springframework.data.spel.EvaluationContextProvider;\nimport org.springframework.data.util.Lazy;\nimport org.springframework.expression.spel.standard.SpelExpression;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\n\n/**\n * {@link RepositoryQuery} implementation deriving queries from {@link PartTree} using a predefined\n * {@link AbstractQueryCreator}.\n *\n * @author Christoph Strobl\n * @author Oliver Gierke\n * @author Mark Paluch\n */\npublic class KeyValuePartTreeQuery implements RepositoryQuery {\n\n\tprivate final Lazy<PartTree> partTree;\n\tprivate final QueryMethod queryMethod;\n\tprivate final KeyValueOperations keyValueOperations;\n\tprivate final ValueExpressionDelegate valueExpressionDelegate;\n\tprivate final QueryCreatorFactory<AbstractQueryCreator<KeyValueQuery<?>, ?>> queryCreatorFactory;\n\tprivate final ValueEvaluationContextProvider evaluationContextProvider;\n\n\t/**\n\t * Creates a new {@link KeyValuePartTreeQuery} for the given {@link QueryMethod}, {@link EvaluationContextProvider},\n\t * {@link KeyValueOperations} and query creator type.\n\t *\n\t * @param queryMethod must not be {@literal null}.\n\t * @param valueExpressionDelegate must not be {@literal null}.\n\t * @param keyValueOperations must not be {@literal null}.\n\t * @param queryCreator must not be {@literal null}.\n\t */\n\tpublic KeyValuePartTreeQuery(QueryMethod queryMethod, ValueExpressionDelegate valueExpressionDelegate,\n\t\t\tKeyValueOperations keyValueOperations, Class<? extends AbstractQueryCreator<?, ?>> queryCreator) {\n\n\t\tthis(queryMethod, valueExpressionDelegate, keyValueOperations,\n\t\t\t\tnew ConstructorCachingQueryCreatorFactory(queryCreator));\n\t}\n\n\t/**\n\t * Creates a new {@link KeyValuePartTreeQuery} for the given {@link QueryMethod}, {@link EvaluationContextProvider},\n\t * {@link KeyValueOperations} using the given {@link QueryCreatorFactory} producing the {@link AbstractQueryCreator}\n\t * in charge of altering the query.\n\t *\n\t * @param queryMethod must not be {@literal null}.\n\t * @param valueExpressionDelegate must not be {@literal null}.\n\t * @param keyValueOperations must not be {@literal null}.\n\t * @param queryCreatorFactory must not be {@literal null}.\n\t * @since 2.0\n\t */\n\tpublic KeyValuePartTreeQuery(QueryMethod queryMethod, ValueExpressionDelegate valueExpressionDelegate,\n\t\t\tKeyValueOperations keyValueOperations,\n\t\t\tQueryCreatorFactory<AbstractQueryCreator<KeyValueQuery<?>, ?>> queryCreatorFactory) {\n\n\t\tAssert.notNull(queryMethod, \"Query method must not be null\");\n\t\tAssert.notNull(valueExpressionDelegate, \"ValueExpressionDelegate must not be null\");\n\t\tAssert.notNull(keyValueOperations, \"KeyValueOperations must not be null\");\n\t\tAssert.notNull(queryCreatorFactory, \"QueryCreatorFactory type must not be null\");\n\n\t\tthis.partTree = Lazy\n\t\t\t\t.of(() -> new PartTree(queryMethod.getName(), queryMethod.getEntityInformation().getJavaType()));\n\t\tthis.queryMethod = queryMethod;\n\t\tthis.keyValueOperations = keyValueOperations;\n\t\tthis.valueExpressionDelegate = valueExpressionDelegate;\n\t\tthis.queryCreatorFactory = queryCreatorFactory;\n\t\tthis.evaluationContextProvider = valueExpressionDelegate.createValueContextProvider(queryMethod.getParameters());\n\t}\n\n\t@Override\n\tpublic @Nullable Object execute(Object[] parameters) {\n\n\t\tParameterAccessor accessor = new ParametersParameterAccessor(getQueryMethod().getParameters(), parameters);\n\t\tKeyValueQuery<?> query = prepareQuery(parameters);\n\t\tResultProcessor processor = queryMethod.getResultProcessor().withDynamicProjection(accessor);\n\n\t\treturn processor.processResult(doExecute(parameters, query));\n\t}\n\n\t/**\n\t * @param parameters\n\t * @param query\n\t */\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\tprotected @Nullable Object doExecute(Object[] parameters, KeyValueQuery<?> query) {\n\n\t\tif (queryMethod.isPageQuery() || queryMethod.isSliceQuery()) {\n\n\t\t\tPageable page = (Pageable) parameters[queryMethod.getParameters().getPageableIndex()];\n\t\t\tquery.setOffset(page.getOffset());\n\t\t\tquery.setRows(page.getPageSize());\n\n\t\t\tIterable<?> result = this.keyValueOperations.find(query, queryMethod.getEntityInformation().getJavaType());\n\n\t\t\tlong count = queryMethod.isSliceQuery() ? 0\n\t\t\t\t\t: keyValueOperations.count(query, queryMethod.getEntityInformation().getJavaType());\n\n\t\t\treturn new PageImpl(IterableConverter.toList(result), page, count);\n\t\t} else if (queryMethod.isCollectionQuery()) {\n\t\t\treturn this.keyValueOperations.find(query, queryMethod.getEntityInformation().getJavaType());\n\t\t} else if (partTree.get().isExistsProjection()) {\n\t\t\treturn keyValueOperations.exists(query, queryMethod.getEntityInformation().getJavaType());\n\t\t} else if (partTree.get().isCountProjection()) {\n\t\t\treturn keyValueOperations.count(query, queryMethod.getEntityInformation().getJavaType());\n\t\t} else {\n\n\t\t\tIterable<?> result = this.keyValueOperations.find(query, queryMethod.getEntityInformation().getJavaType());\n\t\t\treturn result.iterator().hasNext() ? result.iterator().next() : null;\n\t\t}\n\t}\n\n\tprotected KeyValueQuery<?> prepareQuery(Object[] parameters) {\n\n\t\treturn prepareQuery(createQuery(new ParametersParameterAccessor(getQueryMethod().getParameters(), parameters)),\n\t\t\t\tparameters);\n\t}\n\n\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\tprotected KeyValueQuery<?> prepareQuery(KeyValueQuery<?> instance, Object[] parameters) {\n\n\t\tParametersParameterAccessor accessor = new ParametersParameterAccessor(getQueryMethod().getParameters(),\n\t\t\t\tparameters);\n\n\t\tObject criteria = instance.getCriteria();\n\n\t\tif (criteria instanceof SpelCriteria || criteria instanceof SpelExpression) {\n\n\t\t\tSpelExpression spelExpression = getSpelExpression(criteria);\n\t\t\tValueEvaluationContext context = this.evaluationContextProvider.getEvaluationContext(parameters);\n\t\t\tcriteria = new SpelCriteria(spelExpression, context.getRequiredEvaluationContext());\n\t\t}\n\n\t\tKeyValueQuery<?> query = new KeyValueQuery(criteria);\n\t\tPageable pageable = accessor.getPageable();\n\t\tSort sort = accessor.getSort();\n\n\t\tquery.setOffset(pageable.toOptional().map(Pageable::getOffset).orElse(-1L));\n\n\t\tif (pageable.isPaged()) {\n\t\t\tquery.setRows(pageable.getPageSize());\n\t\t} else if (instance.getRows() >= 0) {\n\t\t\tquery.setRows(instance.getRows());\n\t\t}\n\n\t\tquery.setSort(sort.isUnsorted() ? instance.getSort() : sort);\n\n\t\treturn query;\n\t}\n\n\tprivate SpelExpression getSpelExpression(Object criteria) {\n\n\t\tif (criteria instanceof SpelExpression) {\n\t\t\treturn (SpelExpression) criteria;\n\t\t}\n\n\t\tif (criteria instanceof SpelCriteria) {\n\t\t\treturn getSpelExpression(((SpelCriteria) criteria).getExpression());\n\t\t}\n\n\t\tthrow new IllegalStateException(String.format(\"Cannot retrieve SpelExpression from %s\", criteria));\n\t}\n\n\t/**\n\t * Create a {@link KeyValueQuery} given {@link ParameterAccessor}.\n\t *\n\t * @param accessor must not be {@literal null}.\n\t * @return the {@link KeyValueQuery}.\n\t */\n\t@SuppressWarnings(\"NullAway\")\n\tpublic KeyValueQuery<?> createQuery(ParameterAccessor accessor) {\n\n\t\tPartTree tree = this.partTree.get();\n\t\tAbstractQueryCreator<? extends KeyValueQuery<?>, ?> queryCreator = queryCreatorFactory.queryCreatorFor(tree,\n\t\t\t\taccessor);\n\n\t\tKeyValueQuery<?> query = queryCreator.createQuery();\n\n\t\tif (tree.isLimiting()) {\n\t\t\tquery.setRows(tree.getMaxResults());\n\t\t}\n\t\treturn query;\n\t}\n\n\t@Override\n\tpublic QueryMethod getQueryMethod() {\n\t\treturn queryMethod;\n\t}\n\n\t/**\n\t * Factory class for obtaining {@link AbstractQueryCreator} instances for a given {@link PartTree} and\n\t * {@link ParameterAccessor}.\n\t *\n\t * @author Christoph Strobl\n\t * @since 2.0\n\t */\n\tpublic interface QueryCreatorFactory<T extends AbstractQueryCreator<?, ?>> {\n\n\t\tT queryCreatorFor(PartTree partTree, ParameterAccessor accessor);\n\t}\n\n\t/**\n\t * {@link QueryCreatorFactory} implementation instantiating {@link AbstractQueryCreator} via reflection. Looks up and\n\t * caches the constructor on creation.\n\t *\n\t * @author Christoph Strobl\n\t * @since 2.0\n\t */\n\tprivate static class ConstructorCachingQueryCreatorFactory\n\t\t\timplements QueryCreatorFactory<AbstractQueryCreator<KeyValueQuery<?>, ?>> {\n\n\t\tprivate final Class<?> type;\n\t\tprivate final @Nullable Constructor<? extends AbstractQueryCreator<?, ?>> constructor;\n\n\t\tConstructorCachingQueryCreatorFactory(Class<? extends AbstractQueryCreator<?, ?>> type) {\n\n\t\t\tthis.type = type;\n\t\t\tthis.constructor = ClassUtils.getConstructorIfAvailable(type, PartTree.class, ParameterAccessor.class);\n\t\t}\n\n\t\t@Override\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tpublic AbstractQueryCreator<KeyValueQuery<?>, ?> queryCreatorFor(PartTree partTree, ParameterAccessor accessor) {\n\n\t\t\tAssert.state(constructor != null,\n\t\t\t\t\t() -> String.format(\"No constructor (PartTree, ParameterAccessor) could be found on type %s\", type));\n\t\t\treturn (AbstractQueryCreator<KeyValueQuery<?>, ?>) BeanUtils.instantiateClass(constructor, partTree, accessor);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/repository/query/PredicateQueryCreator.java",
    "content": "/*\n * Copyright 2024-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.repository.query;\n\nimport java.util.Collection;\nimport java.util.Comparator;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.regex.Pattern;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.dao.InvalidDataAccessApiUsageException;\nimport org.springframework.data.core.PropertyPath;\nimport org.springframework.data.domain.Sort;\nimport org.springframework.data.keyvalue.core.SimplePropertyPathAccessor;\nimport org.springframework.data.keyvalue.core.query.KeyValueQuery;\nimport org.springframework.data.repository.query.ParameterAccessor;\nimport org.springframework.data.repository.query.parser.AbstractQueryCreator;\nimport org.springframework.data.repository.query.parser.Part;\nimport org.springframework.data.repository.query.parser.Part.IgnoreCaseType;\nimport org.springframework.data.repository.query.parser.PartTree;\nimport org.springframework.lang.Contract;\nimport org.springframework.util.ObjectUtils;\n\n/**\n * {@link AbstractQueryCreator} to create {@link Predicate}-based {@link KeyValueQuery}s.\n *\n * @author Christoph Strobl\n * @author Tom Van Wemmel\n * @author Mark Paluch\n * @since 3.3\n */\npublic class PredicateQueryCreator extends AbstractQueryCreator<KeyValueQuery<Predicate<?>>, Predicate<?>> {\n\n\tprivate static final Comparator<?> COMPARATOR = Comparator.nullsFirst(Comparator.naturalOrder());\n\n\tpublic PredicateQueryCreator(PartTree tree, ParameterAccessor parameters) {\n\t\tsuper(tree, parameters);\n\t}\n\n\t@Override\n\tprotected Predicate<?> create(Part part, Iterator<Object> iterator) {\n\n\t\tPredicateBuilder builder = PredicateBuilder.propertyValueOf(part);\n\n\t\treturn switch (part.getType()) {\n\t\t\tcase TRUE -> builder.isTrue();\n\t\t\tcase FALSE -> builder.isFalse();\n\t\t\tcase SIMPLE_PROPERTY -> builder.isEqualTo(iterator.next());\n\t\t\tcase NEGATING_SIMPLE_PROPERTY -> builder.isEqualTo(iterator.next()).negate();\n\t\t\tcase IS_NULL -> builder.isNull();\n\t\t\tcase IS_NOT_NULL -> builder.isNotNull();\n\t\t\tcase LIKE -> builder.contains(iterator.next());\n\t\t\tcase NOT_LIKE -> builder.contains(iterator.next()).negate();\n\t\t\tcase STARTING_WITH -> builder.startsWith(iterator.next());\n\t\t\tcase AFTER, GREATER_THAN -> builder.isGreaterThan(iterator.next());\n\t\t\tcase GREATER_THAN_EQUAL -> builder.isGreaterThanEqual(iterator.next());\n\t\t\tcase BEFORE, LESS_THAN -> builder.isLessThan(iterator.next());\n\t\t\tcase LESS_THAN_EQUAL -> builder.isLessThanEqual(iterator.next());\n\t\t\tcase ENDING_WITH -> builder.endsWith(iterator.next());\n\t\t\tcase BETWEEN -> builder.isGreaterThan(iterator.next()).and(builder.isLessThan(iterator.next()));\n\t\t\tcase REGEX -> builder.matches(iterator.next());\n\t\t\tcase IN -> builder.in(iterator.next());\n\t\t\tcase NOT_IN -> builder.in(iterator.next()).negate();\n\t\t\tdefault ->\n\t\t\t\tthrow new InvalidDataAccessApiUsageException(String.format(\"Found invalid part '%s' in query\", part.getType()));\n\t\t};\n\t}\n\n\t@Override\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\tprotected Predicate<?> and(Part part, Predicate<?> base, Iterator<Object> iterator) {\n\t\treturn base.and((Predicate) create(part, iterator));\n\t}\n\n\t@Override\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\tprotected Predicate<?> or(Predicate<?> base, Predicate<?> criteria) {\n\t\treturn base.or((Predicate) criteria);\n\t}\n\n\t@Override\n\tprotected KeyValueQuery<Predicate<?>> complete(@Nullable Predicate<?> criteria, Sort sort) {\n\t\treturn criteria == null ? new KeyValueQuery<>(it -> true, sort) : new KeyValueQuery<>(criteria, sort);\n\t}\n\n\tstatic class PredicateBuilder {\n\n\t\tprivate final Part part;\n\n\t\tpublic PredicateBuilder(Part part) {\n\t\t\tthis.part = part;\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tstatic <T> Comparator<T> comparator() {\n\t\t\treturn (Comparator<T>) COMPARATOR;\n\t\t}\n\n\t\tstatic PredicateBuilder propertyValueOf(Part part) {\n\t\t\treturn new PredicateBuilder(part);\n\t\t}\n\n\t\tpublic Predicate<Object> isTrue() {\n\t\t\treturn new ValueComparingPredicate(part.getProperty(), true);\n\t\t}\n\n\t\tpublic Predicate<Object> isFalse() {\n\t\t\treturn new ValueComparingPredicate(part.getProperty(), false);\n\t\t}\n\n\t\t@Contract(\"_ -> new\")\n\t\tpublic Predicate<Object> isEqualTo(@Nullable Object value) {\n\t\t\treturn new ValueComparingPredicate(part.getProperty(), o -> {\n\n\t\t\t\tif (!ObjectUtils.nullSafeEquals(IgnoreCaseType.NEVER, part.shouldIgnoreCase())) {\n\t\t\t\t\tif (o instanceof String s1 && value instanceof String s2) {\n\t\t\t\t\t\treturn s1.equalsIgnoreCase(s2);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn ObjectUtils.nullSafeEquals(o, value);\n\n\t\t\t});\n\t\t}\n\n\t\tpublic Predicate<Object> isNull() {\n\t\t\treturn new ValueComparingPredicate(part.getProperty(), Objects::isNull);\n\t\t}\n\n\t\tpublic Predicate<Object> isNotNull() {\n\t\t\treturn isNull().negate();\n\t\t}\n\n\t\t@Contract(\"_ -> new\")\n\t\tpublic Predicate<Object> isLessThan(@Nullable Object value) {\n\t\t\treturn new ValueComparingPredicate(part.getProperty(), o -> comparator().compare(o, value) < 0);\n\t\t}\n\n\t\t@Contract(\"_ -> new\")\n\t\tpublic Predicate<Object> isLessThanEqual(@Nullable Object value) {\n\t\t\treturn new ValueComparingPredicate(part.getProperty(), o -> comparator().compare(o, value) <= 0);\n\t\t}\n\n\t\t@Contract(\"_ -> new\")\n\t\tpublic Predicate<Object> isGreaterThan(@Nullable Object value) {\n\t\t\treturn new ValueComparingPredicate(part.getProperty(), o -> comparator().compare(o, value) > 0);\n\t\t}\n\n\t\t@Contract(\"_ -> new\")\n\t\tpublic Predicate<Object> isGreaterThanEqual(@Nullable Object value) {\n\t\t\treturn new ValueComparingPredicate(part.getProperty(), o -> comparator().compare(o, value) >= 0);\n\t\t}\n\n\t\t@Contract(\"!null -> new\")\n\t\tpublic Predicate<Object> matches(Pattern pattern) {\n\n\t\t\treturn new ValueComparingPredicate(part.getProperty(), o -> {\n\t\t\t\tif (o == null) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\treturn pattern.matcher(o.toString()).find();\n\t\t\t});\n\t\t}\n\n\t\t@Contract(\"_ -> new\")\n\t\tpublic Predicate<Object> matches(@Nullable Object value) {\n\t\t\treturn new ValueComparingPredicate(part.getProperty(), o -> {\n\n\t\t\t\tif (o == null || value == null) {\n\t\t\t\t\treturn ObjectUtils.nullSafeEquals(o, value);\n\t\t\t\t}\n\n\t\t\t\tif (value instanceof Pattern pattern) {\n\t\t\t\t\treturn pattern.matcher(o.toString()).find();\n\t\t\t\t}\n\n\t\t\t\treturn o.toString().matches(value.toString());\n\n\t\t\t});\n\t\t}\n\n\t\t@Contract(\"!null -> new\")\n\t\tpublic Predicate<Object> matches(String regex) {\n\t\t\treturn matches(Pattern.compile(regex));\n\t\t}\n\n\t\t@Contract(\"!null -> new\")\n\t\tpublic Predicate<Object> in(Object value) {\n\t\t\treturn new ValueComparingPredicate(part.getProperty(), o -> {\n\n\t\t\t\tif (value instanceof Collection<?> collection) {\n\n\t\t\t\t\tif (o instanceof Collection<?> subSet) {\n\t\t\t\t\t\treturn collection.containsAll(subSet);\n\t\t\t\t\t}\n\n\t\t\t\t\treturn collection.contains(o);\n\t\t\t\t}\n\t\t\t\tif (ObjectUtils.isArray(value)) {\n\t\t\t\t\treturn ObjectUtils.containsElement(ObjectUtils.toObjectArray(value), value);\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t});\n\t\t}\n\n\t\t@Contract(\"_ -> new\")\n\t\tpublic Predicate<Object> contains(@Nullable Object value) {\n\n\t\t\treturn new ValueComparingPredicate(part.getProperty(), o -> {\n\n\t\t\t\tif (o == null) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tif (o instanceof Collection<?> collection) {\n\t\t\t\t\treturn collection.contains(value);\n\t\t\t\t}\n\n\t\t\t\tif (ObjectUtils.isArray(o)) {\n\t\t\t\t\treturn ObjectUtils.containsElement(ObjectUtils.toObjectArray(o), value);\n\t\t\t\t}\n\n\t\t\t\tif (o instanceof Map<?, ?> map) {\n\t\t\t\t\treturn map.containsValue(value);\n\t\t\t\t}\n\n\t\t\t\tif (value == null) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tString s = o.toString();\n\n\t\t\t\tif (ObjectUtils.nullSafeEquals(IgnoreCaseType.NEVER, part.shouldIgnoreCase())) {\n\t\t\t\t\treturn s.contains(value.toString());\n\t\t\t\t}\n\t\t\t\treturn s.toLowerCase().contains(value.toString().toLowerCase());\n\n\t\t\t});\n\t\t}\n\n\t\t@Contract(\"!null -> new\")\n\t\tpublic Predicate<Object> startsWith(Object value) {\n\t\t\treturn new ValueComparingPredicate(part.getProperty(), o -> {\n\n\t\t\t\tif (!(o instanceof String s)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tif (ObjectUtils.nullSafeEquals(IgnoreCaseType.NEVER, part.shouldIgnoreCase())) {\n\t\t\t\t\treturn s.startsWith(value.toString());\n\t\t\t\t}\n\n\t\t\t\treturn s.toLowerCase().startsWith(value.toString().toLowerCase());\n\t\t\t});\n\n\t\t}\n\n\t\t@Contract(\"!null -> new\")\n\t\tpublic Predicate<Object> endsWith(Object value) {\n\n\t\t\treturn new ValueComparingPredicate(part.getProperty(), o -> {\n\n\t\t\t\tif (!(o instanceof String s)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tif (ObjectUtils.nullSafeEquals(IgnoreCaseType.NEVER, part.shouldIgnoreCase())) {\n\t\t\t\t\treturn s.endsWith(value.toString());\n\t\t\t\t}\n\n\t\t\t\treturn s.toLowerCase().endsWith(value.toString().toLowerCase());\n\t\t\t});\n\t\t}\n\t}\n\n\tstatic class ValueComparingPredicate implements Predicate<Object> {\n\n\t\tprivate final PropertyPath path;\n\t\tprivate final Function<@Nullable Object, Boolean> check;\n\n\t\tpublic ValueComparingPredicate(PropertyPath path, @Nullable Object expected) {\n\t\t\tthis(path, (value) -> ObjectUtils.nullSafeEquals(value, expected));\n\t\t}\n\n\t\tpublic ValueComparingPredicate(PropertyPath path, Function<@Nullable Object, Boolean> check) {\n\t\t\tthis.path = path;\n\t\t\tthis.check = check;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean test(Object o) {\n\t\t\tObject value = new SimplePropertyPathAccessor<>(o).getValue(path);\n\t\t\treturn check.apply(value);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/repository/query/SpelQueryCreator.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.repository.query;\n\nimport java.util.Iterator;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.dao.InvalidDataAccessApiUsageException;\nimport org.springframework.data.domain.Sort;\nimport org.springframework.data.keyvalue.core.query.KeyValueQuery;\nimport org.springframework.data.repository.query.ParameterAccessor;\nimport org.springframework.data.repository.query.parser.AbstractQueryCreator;\nimport org.springframework.data.repository.query.parser.Part;\nimport org.springframework.data.repository.query.parser.Part.IgnoreCaseType;\nimport org.springframework.data.repository.query.parser.Part.Type;\nimport org.springframework.data.repository.query.parser.PartTree;\nimport org.springframework.data.repository.query.parser.PartTree.OrPart;\nimport org.springframework.expression.spel.standard.SpelExpression;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.util.StringUtils;\n\n/**\n * {@link AbstractQueryCreator} to create {@link SpelExpression}-based {@link KeyValueQuery}s.\n *\n * @author Christoph Strobl\n * @author Oliver Gierke\n * @author Mark Paluch\n * @author Tom Van Wemmel\n */\npublic class SpelQueryCreator extends AbstractQueryCreator<KeyValueQuery<SpelExpression>, String> {\n\n\tprivate static final SpelExpressionParser PARSER = new SpelExpressionParser();\n\n\tprivate final SpelExpression expression;\n\n\t/**\n\t * Creates a new {@link SpelQueryCreator} for the given {@link PartTree} and {@link ParameterAccessor}.\n\t *\n\t * @param tree must not be {@literal null}.\n\t * @param parameters must not be {@literal null}.\n\t */\n\tpublic SpelQueryCreator(PartTree tree, ParameterAccessor parameters) {\n\n\t\tsuper(tree, parameters);\n\n\t\tthis.expression = toPredicateExpression(tree);\n\t}\n\n\t@Override\n\tprotected String create(Part part, Iterator<Object> iterator) {\n\t\treturn \"\";\n\t}\n\n\t@Override\n\tprotected String and(Part part, String base, Iterator<Object> iterator) {\n\t\treturn \"\";\n\t}\n\n\t@Override\n\tprotected String or(String base, String criteria) {\n\t\treturn \"\";\n\t}\n\n\t@Override\n\tprotected KeyValueQuery<SpelExpression> complete(@Nullable String criteria, Sort sort) {\n\n\t\tKeyValueQuery<SpelExpression> query = new KeyValueQuery<>(this.expression);\n\n\t\tif (sort.isSorted()) {\n\t\t\tquery.orderBy(sort);\n\t\t}\n\n\t\treturn query;\n\t}\n\n\tprotected SpelExpression toPredicateExpression(PartTree tree) {\n\n\t\tint parameterIndex = 0;\n\t\tStringBuilder sb = new StringBuilder();\n\n\t\tfor (Iterator<OrPart> orPartIter = tree.iterator(); orPartIter.hasNext();) {\n\n\t\t\tint partCnt = 0;\n\t\t\tStringBuilder partBuilder = new StringBuilder();\n\t\t\tOrPart orPart = orPartIter.next();\n\n\t\t\tfor (Iterator<Part> partIter = orPart.iterator(); partIter.hasNext();) {\n\n\t\t\t\tPart part = partIter.next();\n\n\t\t\t\tif (!requiresInverseLookup(part)) {\n\n\t\t\t\t\tpartBuilder.append(\"#it?.\");\n\t\t\t\t\tpartBuilder.append(part.getProperty().toDotPath().replace(\".\", \"?.\"));\n\t\t\t\t}\n\n\t\t\t\t// TODO: check if we can have caseinsensitive search\n\t\t\t\tif (!part.shouldIgnoreCase().equals(IgnoreCaseType.NEVER)) {\n\t\t\t\t\tthrow new InvalidDataAccessApiUsageException(\"Ignore case not supported\");\n\t\t\t\t}\n\n\t\t\t\tswitch (part.getType()) {\n\t\t\t\t\tcase TRUE:\n\t\t\t\t\t\tpartBuilder.append(\"?.equals(true)\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase FALSE:\n\t\t\t\t\t\tpartBuilder.append(\"?.equals(false)\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SIMPLE_PROPERTY:\n\t\t\t\t\tcase NEGATING_SIMPLE_PROPERTY:\n\n\t\t\t\t\t\tpartBuilder.append(\"?.equals(\").append(\"[\").append(parameterIndex++).append(\"])\");\n\n\t\t\t\t\t\tif (part.getType() == Type.NEGATING_SIMPLE_PROPERTY) {\n\t\t\t\t\t\t\tpartBuilder.append(\" == false\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_NULL:\n\t\t\t\t\t\tpartBuilder.append(\" == null\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_NOT_NULL:\n\t\t\t\t\t\tpartBuilder.append(\" != null\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase LIKE:\n\t\t\t\t\tcase NOT_LIKE:\n\n\t\t\t\t\t\tpartBuilder.append(\"?.contains(\").append(\"[\").append(parameterIndex++).append(\"])\");\n\n\t\t\t\t\t\tif (part.getType() == Type.NOT_LIKE) {\n\t\t\t\t\t\t\tpartBuilder.append(\" == false\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase STARTING_WITH:\n\t\t\t\t\t\tpartBuilder.append(\"?.startsWith(\").append(\"[\").append(parameterIndex++).append(\"])\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase AFTER:\n\t\t\t\t\tcase GREATER_THAN:\n\t\t\t\t\t\tpartBuilder.append(\">\").append(\"[\").append(parameterIndex++).append(\"]\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase GREATER_THAN_EQUAL:\n\t\t\t\t\t\tpartBuilder.append(\">=\").append(\"[\").append(parameterIndex++).append(\"]\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase BEFORE:\n\t\t\t\t\tcase LESS_THAN:\n\t\t\t\t\t\tpartBuilder.append(\"<\").append(\"[\").append(parameterIndex++).append(\"]\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase LESS_THAN_EQUAL:\n\t\t\t\t\t\tpartBuilder.append(\"<=\").append(\"[\").append(parameterIndex++).append(\"]\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ENDING_WITH:\n\t\t\t\t\t\tpartBuilder.append(\"?.endsWith(\").append(\"[\").append(parameterIndex++).append(\"])\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase BETWEEN:\n\n\t\t\t\t\t\tint index = partBuilder.lastIndexOf(\"#it?.\");\n\n\t\t\t\t\t\tpartBuilder.insert(index, \"(\");\n\t\t\t\t\t\tpartBuilder.append(\">\").append(\"[\").append(parameterIndex++).append(\"]\");\n\t\t\t\t\t\tpartBuilder.append(\"&&\");\n\t\t\t\t\t\tpartBuilder.append(\"#it?.\");\n\t\t\t\t\t\tpartBuilder.append(part.getProperty().toDotPath().replace(\".\", \"?.\"));\n\t\t\t\t\t\tpartBuilder.append(\"<\").append(\"[\").append(parameterIndex++).append(\"]\");\n\t\t\t\t\t\tpartBuilder.append(\")\");\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase REGEX:\n\n\t\t\t\t\t\tpartBuilder.append(\" matches \").append(\"[\").append(parameterIndex++).append(\"]\");\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase NOT_IN:\n\t\t\t\t\tcase IN:\n\n\t\t\t\t\t\tpartBuilder.append(\"[\").append(parameterIndex++).append(\"].contains(\");\n\t\t\t\t\t\tpartBuilder.append(\"#it?.\");\n\t\t\t\t\t\tpartBuilder.append(part.getProperty().toDotPath().replace(\".\", \"?.\"));\n\t\t\t\t\t\tpartBuilder.append(\")\");\n\n\t\t\t\t\t\tif (part.getType() == Type.NOT_IN) {\n\t\t\t\t\t\t\tpartBuilder.append(\" == false\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase CONTAINING:\n\t\t\t\t\tcase NOT_CONTAINING:\n\t\t\t\t\tcase EXISTS:\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tthrow new InvalidDataAccessApiUsageException(\"Found invalid part '%s' in query\".formatted(part.getType()));\n\t\t\t\t}\n\n\t\t\t\tif (partIter.hasNext()) {\n\t\t\t\t\tpartBuilder.append(\" && \");\n\t\t\t\t}\n\n\t\t\t\tpartCnt++;\n\t\t\t}\n\n\t\t\tif (partCnt > 1) {\n\t\t\t\tsb.append(\"(\").append(partBuilder).append(\")\");\n\t\t\t} else {\n\t\t\t\tsb.append(partBuilder);\n\t\t\t}\n\n\t\t\tif (orPartIter.hasNext()) {\n\t\t\t\tsb.append(\" || \");\n\t\t\t}\n\t\t}\n\n\t\treturn StringUtils.hasText(sb) ? PARSER.parseRaw(sb.toString()) : PARSER.parseRaw(\"true\");\n\t}\n\n\tprivate static boolean requiresInverseLookup(Part part) {\n\t\treturn part.getType() == Type.IN || part.getType() == Type.NOT_IN;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/repository/query/package-info.java",
    "content": "/**\n * Query derivation mechanism for key/value specific repositories providing a generic SpEL based implementation.\n */\n@org.jspecify.annotations.NullMarked\npackage org.springframework.data.keyvalue.repository.query;\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/repository/support/KeyValueQuerydslUtils.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.repository.support;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.springframework.data.core.PropertyPath;\nimport org.springframework.data.domain.Sort;\nimport org.springframework.data.domain.Sort.Order;\nimport org.springframework.data.querydsl.QSort;\nimport org.springframework.lang.Contract;\nimport org.springframework.util.Assert;\n\nimport com.querydsl.core.types.Expression;\nimport com.querydsl.core.types.OrderSpecifier;\nimport com.querydsl.core.types.OrderSpecifier.NullHandling;\nimport com.querydsl.core.types.Path;\nimport com.querydsl.core.types.dsl.Expressions;\nimport com.querydsl.core.types.dsl.PathBuilder;\n\n/**\n * Utilities for Querydsl usage.\n *\n * @author Christoph Strobl\n * @author Thomas Darimont\n * @author Oliver Gierke\n * @author Mark Paluch\n */\nabstract class KeyValueQuerydslUtils {\n\n\tprivate KeyValueQuerydslUtils() {\n\t\t// prevent instantiation\n\t}\n\n\t/**\n\t * Transforms a plain {@link Order} into a QueryDsl specific {@link OrderSpecifier}.\n\t *\n\t * @param sort must not be {@literal null}.\n\t * @param builder must not be {@literal null}.\n\t * @return empty {@code OrderSpecifier<?>[]} when sort is {@literal null}.\n\t */\n\t@Contract(\"!null, !null -> new\")\n\tstatic OrderSpecifier<?>[] toOrderSpecifier(Sort sort, PathBuilder<?> builder) {\n\n\t\tAssert.notNull(sort, \"Sort must not be null\");\n\t\tAssert.notNull(builder, \"Builder must not be null\");\n\n\t\tList<OrderSpecifier<?>> specifiers = null;\n\n\t\tif (sort instanceof QSort) {\n\t\t\tspecifiers = ((QSort) sort).getOrderSpecifiers();\n\t\t} else {\n\n\t\t\tspecifiers = new ArrayList<>();\n\t\t\tfor (Order order : sort) {\n\t\t\t\tspecifiers.add(toOrderSpecifier(order, builder));\n\t\t\t}\n\t\t}\n\n\t\treturn specifiers.toArray(new OrderSpecifier<?>[specifiers.size()]);\n\t}\n\n\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\tprivate static OrderSpecifier<?> toOrderSpecifier(Order order, PathBuilder<?> builder) {\n\t\treturn new OrderSpecifier(\n\t\t\t\torder.isAscending() ? com.querydsl.core.types.Order.ASC : com.querydsl.core.types.Order.DESC,\n\t\t\t\tbuildOrderPropertyPathFrom(order, builder), toQueryDslNullHandling(order.getNullHandling()));\n\t}\n\n\t/**\n\t * Creates an {@link Expression} for the given {@link Order} property.\n\t *\n\t * @param order must not be {@literal null}.\n\t * @param builder must not be {@literal null}.\n\t * @return\n\t */\n\tprivate static Expression<?> buildOrderPropertyPathFrom(Order order, PathBuilder<?> builder) {\n\n\t\tAssert.notNull(order, \"Order must not be null\");\n\t\tAssert.notNull(builder, \"Builder must not be null\");\n\n\t\tPropertyPath path = PropertyPath.from(order.getProperty(), builder.getType());\n\t\tExpression<?> sortPropertyExpression = builder;\n\n\t\twhile (path != null) {\n\n\t\t\tif (!path.hasNext() && order.isIgnoreCase()) {\n\t\t\t\t// if order is ignore-case we have to treat the last path segment as a String.\n\t\t\t\tsortPropertyExpression = Expressions.stringPath((Path<?>) sortPropertyExpression, path.getSegment()).lower();\n\t\t\t} else {\n\t\t\t\tsortPropertyExpression = Expressions.path(path.getType(), (Path<?>) sortPropertyExpression, path.getSegment());\n\t\t\t}\n\n\t\t\tpath = path.next();\n\t\t}\n\n\t\treturn sortPropertyExpression;\n\t}\n\n\t/**\n\t * Converts the given {@link org.springframework.data.domain.Sort.NullHandling} to the appropriate Querydsl\n\t * {@link NullHandling}.\n\t *\n\t * @param nullHandling must not be {@literal null}.\n\t * @return\n\t */\n\tprivate static NullHandling toQueryDslNullHandling(org.springframework.data.domain.Sort.NullHandling nullHandling) {\n\n\t\tAssert.notNull(nullHandling, \"NullHandling must not be null\");\n\n\t\tswitch (nullHandling) {\n\n\t\t\tcase NULLS_FIRST:\n\t\t\t\treturn NullHandling.NullsFirst;\n\n\t\t\tcase NULLS_LAST:\n\t\t\t\treturn NullHandling.NullsLast;\n\n\t\t\tcase NATIVE:\n\t\t\tdefault:\n\t\t\t\treturn NullHandling.Default;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/repository/support/KeyValueRepositoryFactory.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.repository.support;\n\nimport static org.springframework.data.querydsl.QuerydslUtils.*;\nimport static org.springframework.data.repository.core.support.RepositoryComposition.*;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Method;\nimport java.util.Optional;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.dao.InvalidDataAccessApiUsageException;\nimport org.springframework.data.keyvalue.core.KeyValueOperations;\nimport org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery;\nimport org.springframework.data.keyvalue.repository.query.PredicateQueryCreator;\nimport org.springframework.data.mapping.PersistentEntity;\nimport org.springframework.data.mapping.context.MappingContext;\nimport org.springframework.data.projection.ProjectionFactory;\nimport org.springframework.data.querydsl.QuerydslPredicateExecutor;\nimport org.springframework.data.querydsl.SimpleEntityPathResolver;\nimport org.springframework.data.repository.core.EntityInformation;\nimport org.springframework.data.repository.core.NamedQueries;\nimport org.springframework.data.repository.core.RepositoryInformation;\nimport org.springframework.data.repository.core.RepositoryMetadata;\nimport org.springframework.data.repository.core.support.PersistentEntityInformation;\nimport org.springframework.data.repository.core.support.RepositoryFactorySupport;\nimport org.springframework.data.repository.query.QueryLookupStrategy;\nimport org.springframework.data.repository.query.QueryLookupStrategy.Key;\nimport org.springframework.data.repository.query.QueryMethod;\nimport org.springframework.data.repository.query.RepositoryQuery;\nimport org.springframework.data.repository.query.ValueExpressionDelegate;\nimport org.springframework.data.repository.query.parser.AbstractQueryCreator;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\n\n/**\n * {@link RepositoryFactorySupport} specific of handing\n * {@link org.springframework.data.keyvalue.repository.KeyValueRepository}.\n *\n * @author Christoph Strobl\n * @author Oliver Gierke\n * @author Mark Paluch\n */\npublic class KeyValueRepositoryFactory extends RepositoryFactorySupport {\n\n\tprivate static final Class<PredicateQueryCreator> DEFAULT_QUERY_CREATOR = PredicateQueryCreator.class;\n\n\tprivate final KeyValueOperations keyValueOperations;\n\tprivate final MappingContext<?, ?> context;\n\tprivate final Class<? extends AbstractQueryCreator<?, ?>> queryCreator;\n\tprivate final Class<? extends RepositoryQuery> repositoryQueryType;\n\n\t/**\n\t * Creates a new {@link KeyValueRepositoryFactory} for the given {@link KeyValueOperations}.\n\t *\n\t * @param keyValueOperations must not be {@literal null}.\n\t */\n\tpublic KeyValueRepositoryFactory(KeyValueOperations keyValueOperations) {\n\t\tthis(keyValueOperations, DEFAULT_QUERY_CREATOR);\n\t}\n\n\t/**\n\t * Creates a new {@link KeyValueRepositoryFactory} for the given {@link KeyValueOperations} and\n\t * {@link AbstractQueryCreator}-type.\n\t *\n\t * @param keyValueOperations must not be {@literal null}.\n\t * @param queryCreator must not be {@literal null}.\n\t */\n\tpublic KeyValueRepositoryFactory(KeyValueOperations keyValueOperations,\n\t\t\tClass<? extends AbstractQueryCreator<?, ?>> queryCreator) {\n\n\t\tthis(keyValueOperations, queryCreator, KeyValuePartTreeQuery.class);\n\t}\n\n\t/**\n\t * Creates a new {@link KeyValueRepositoryFactory} for the given {@link KeyValueOperations} and\n\t * {@link AbstractQueryCreator}-type.\n\t *\n\t * @param keyValueOperations must not be {@literal null}.\n\t * @param queryCreator must not be {@literal null}.\n\t * @param repositoryQueryType must not be {@literal null}.\n\t * @since 1.1\n\t */\n\tpublic KeyValueRepositoryFactory(KeyValueOperations keyValueOperations,\n\t\t\tClass<? extends AbstractQueryCreator<?, ?>> queryCreator, Class<? extends RepositoryQuery> repositoryQueryType) {\n\n\t\tAssert.notNull(keyValueOperations, \"KeyValueOperations must not be null\");\n\t\tAssert.notNull(queryCreator, \"Query creator type must not be null\");\n\t\tAssert.notNull(repositoryQueryType, \"RepositoryQueryType type must not be null\");\n\n\t\tthis.queryCreator = queryCreator;\n\t\tthis.keyValueOperations = keyValueOperations;\n\t\tthis.context = keyValueOperations.getMappingContext();\n\t\tthis.repositoryQueryType = repositoryQueryType;\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T, ID> EntityInformation<T, ID> getEntityInformation(Class<T> domainClass) {\n\n\t\tPersistentEntity<T, ?> entity = (PersistentEntity<T, ?>) context.getRequiredPersistentEntity(domainClass);\n\n\t\treturn new PersistentEntityInformation<>(entity);\n\t}\n\n\t@Override\n\tprotected Object getTargetRepository(RepositoryInformation repositoryInformation) {\n\n\t\tEntityInformation<?, ?> entityInformation = getEntityInformation(repositoryInformation.getDomainType());\n\t\treturn super.getTargetRepositoryViaReflection(repositoryInformation, entityInformation, keyValueOperations);\n\t}\n\n\t@Override\n\tprotected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {\n\t\treturn SimpleKeyValueRepository.class;\n\t}\n\n\t@Override\n\tprotected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) {\n\t\treturn getRepositoryFragments(metadata, keyValueOperations);\n\t}\n\n\t/**\n\t * Creates {@link RepositoryFragments} based on {@link RepositoryMetadata} to add Key-Value-specific extensions.\n\t * Typically adds a {@link QuerydslKeyValuePredicateExecutor} if the repository interface uses Querydsl.\n\t * <p>\n\t * Can be overridden by subclasses to customize {@link RepositoryFragments}.\n\t *\n\t * @param metadata repository metadata.\n\t * @param operations the KeyValue operations manager.\n\t * @return\n\t * @since 2.6\n\t */\n\tprotected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata, KeyValueOperations operations) {\n\n\t\tif (isQueryDslRepository(metadata.getRepositoryInterface())) {\n\n\t\t\tif (metadata.isReactiveRepository()) {\n\t\t\t\tthrow new InvalidDataAccessApiUsageException(\n\t\t\t\t\t\t\"Cannot combine Querydsl and reactive repository support in a single interface\");\n\t\t\t}\n\n\t\t\treturn RepositoryFragments\n\t\t\t\t\t.just(new QuerydslKeyValuePredicateExecutor<>(getEntityInformation(metadata.getDomainType()),\n\t\t\t\t\t\t\tgetProjectionFactory(), operations, SimpleEntityPathResolver.INSTANCE));\n\t\t}\n\n\t\treturn RepositoryFragments.empty();\n\t}\n\n\t/**\n\t * Returns whether the given repository interface requires a QueryDsl specific implementation to be chosen.\n\t *\n\t * @param repositoryInterface must not be {@literal null}.\n\t * @return\n\t */\n\tprivate static boolean isQueryDslRepository(Class<?> repositoryInterface) {\n\t\treturn QUERY_DSL_PRESENT && QuerydslPredicateExecutor.class.isAssignableFrom(repositoryInterface);\n\t}\n\n\t@Override\n\tprotected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable Key key,\n\t\t\tValueExpressionDelegate valueExpressionDelegate) {\n\t\treturn Optional.of(new KeyValueQueryLookupStrategy(key, valueExpressionDelegate, this.keyValueOperations,\n\t\t\t\tthis.queryCreator, this.repositoryQueryType));\n\t}\n\n\t/**\n\t * @author Christoph Strobl\n\t * @author Oliver Gierke\n\t */\n\tprivate static class KeyValueQueryLookupStrategy implements QueryLookupStrategy {\n\n\t\tprivate final ValueExpressionDelegate valueExpressionDelegate;\n\t\tprivate final KeyValueOperations keyValueOperations;\n\n\t\tprivate final Class<? extends AbstractQueryCreator<?, ?>> queryCreator;\n\t\tprivate final Class<? extends RepositoryQuery> repositoryQueryType;\n\n\t\t/**\n\t\t * @param key\n\t\t * @param valueExpressionDelegate\n\t\t * @param keyValueOperations\n\t\t * @param queryCreator\n\t\t * @since 1.1\n\t\t */\n\t\tpublic KeyValueQueryLookupStrategy(@Nullable Key key, ValueExpressionDelegate valueExpressionDelegate,\n\t\t\t\tKeyValueOperations keyValueOperations, Class<? extends AbstractQueryCreator<?, ?>> queryCreator,\n\t\t\t\tClass<? extends RepositoryQuery> repositoryQueryType) {\n\n\t\t\tAssert.notNull(valueExpressionDelegate, \"ValueExpressionDelegate must not be null\");\n\t\t\tAssert.notNull(keyValueOperations, \"KeyValueOperations must not be null\");\n\t\t\tAssert.notNull(queryCreator, \"Query creator type must not be null\");\n\t\t\tAssert.notNull(repositoryQueryType, \"RepositoryQueryType type must not be null\");\n\n\t\t\tthis.valueExpressionDelegate = valueExpressionDelegate;\n\t\t\tthis.keyValueOperations = keyValueOperations;\n\t\t\tthis.queryCreator = queryCreator;\n\t\t\tthis.repositoryQueryType = repositoryQueryType;\n\t\t}\n\n\t\t@Override\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tpublic RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory,\n\t\t\t\tNamedQueries namedQueries) {\n\n\t\t\tQueryMethod queryMethod = new QueryMethod(method, metadata, factory);\n\n\t\t\tConstructor<? extends KeyValuePartTreeQuery> constructor = (Constructor<? extends KeyValuePartTreeQuery>) ClassUtils\n\t\t\t\t\t.getConstructorIfAvailable(this.repositoryQueryType, QueryMethod.class, ValueExpressionDelegate.class,\n\t\t\t\t\t\t\tKeyValueOperations.class, Class.class);\n\n\t\t\tAssert.state(constructor != null,\n\t\t\t\t\tString.format(\n\t\t\t\t\t\t\t\"Constructor %s(QueryMethod, EvaluationContextProvider, KeyValueOperations, Class) not available\",\n\t\t\t\t\t\t\tClassUtils.getShortName(this.repositoryQueryType)));\n\n\t\t\treturn BeanUtils.instantiateClass(constructor, queryMethod, valueExpressionDelegate, this.keyValueOperations,\n\t\t\t\t\tthis.queryCreator);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/repository/support/KeyValueRepositoryFactoryBean.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.repository.support;\n\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.data.keyvalue.core.KeyValueOperations;\nimport org.springframework.data.keyvalue.repository.KeyValueRepository;\nimport org.springframework.data.keyvalue.repository.config.QueryCreatorType;\nimport org.springframework.data.mapping.context.MappingContext;\nimport org.springframework.data.repository.Repository;\nimport org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;\nimport org.springframework.data.repository.core.support.RepositoryFactorySupport;\nimport org.springframework.data.repository.query.RepositoryQuery;\nimport org.springframework.data.repository.query.parser.AbstractQueryCreator;\nimport org.springframework.util.Assert;\n\n/**\n * {@link org.springframework.beans.factory.FactoryBean} to create {@link KeyValueRepository}.\n *\n * @author Christoph Strobl\n * @author Oliver Gierke\n * @author Mark Paluch\n */\npublic class KeyValueRepositoryFactoryBean<T extends Repository<S, ID>, S, ID>\n\t\textends RepositoryFactoryBeanSupport<T, S, ID> {\n\n\tprivate @Nullable KeyValueOperations operations;\n\tprivate @Nullable Class<? extends AbstractQueryCreator<?, ?>> queryCreator;\n\tprivate @Nullable Class<? extends RepositoryQuery> repositoryQueryType;\n\n\t/**\n\t * Creates a new {@link KeyValueRepositoryFactoryBean} for the given repository interface.\n\t *\n\t * @param repositoryInterface must not be {@literal null}.\n\t */\n\tpublic KeyValueRepositoryFactoryBean(Class<? extends T> repositoryInterface) {\n\t\tsuper(repositoryInterface);\n\t}\n\n\t/**\n\t * Configures the {@link KeyValueOperations} to be used for the repositories.\n\t *\n\t * @param operations must not be {@literal null}.\n\t */\n\tpublic void setKeyValueOperations(KeyValueOperations operations) {\n\n\t\tAssert.notNull(operations, \"KeyValueOperations must not be null\");\n\n\t\tthis.operations = operations;\n\t}\n\n\t@Override\n\tpublic void setMappingContext(MappingContext<?, ?> mappingContext) {\n\t\tsuper.setMappingContext(mappingContext);\n\t}\n\n\t/**\n\t * Configures the {@link QueryCreatorType} to be used.\n\t *\n\t * @param queryCreator must not be {@literal null}.\n\t */\n\tpublic void setQueryCreator(Class<? extends AbstractQueryCreator<?, ?>> queryCreator) {\n\n\t\tAssert.notNull(queryCreator, \"Query creator type must not be null\");\n\n\t\tthis.queryCreator = queryCreator;\n\t}\n\n\t/**\n\t * Configures the {@link RepositoryQuery} type to be created.\n\t *\n\t * @param repositoryQueryType must not be {@literal null}.\n\t * @since 1.1\n\t */\n\tpublic void setQueryType(Class<? extends RepositoryQuery> repositoryQueryType) {\n\n\t\tAssert.notNull(repositoryQueryType, \"Query creator type must not be null\");\n\n\t\tthis.repositoryQueryType = repositoryQueryType;\n\n\t}\n\n\t@Override\n\tprotected final RepositoryFactorySupport createRepositoryFactory() {\n\n\t\tAssert.notNull(operations, \"Operations must not be null\");\n\t\tAssert.notNull(queryCreator, \"QueryCreator must not be null\");\n\t\tAssert.notNull(repositoryQueryType, \"QueryType must not be null\");\n\n\t\treturn createRepositoryFactory(operations, queryCreator, repositoryQueryType);\n\t}\n\n\t/**\n\t * Create the repository factory to be used to create repositories.\n\t *\n\t * @param operations will never be {@literal null}.\n\t * @param queryCreator will never be {@literal null}.\n\t * @param repositoryQueryType will never be {@literal null}.\n\t * @return must not be {@literal null}.\n\t * @since 1.1\n\t */\n\tprotected KeyValueRepositoryFactory createRepositoryFactory(KeyValueOperations operations,\n\t\t\tClass<? extends AbstractQueryCreator<?, ?>> queryCreator, Class<? extends RepositoryQuery> repositoryQueryType) {\n\n\t\treturn new KeyValueRepositoryFactory(operations, queryCreator, repositoryQueryType);\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\n\t\tAssert.notNull(operations, \"KeyValueOperations must not be null\");\n\t\tAssert.notNull(queryCreator, \"Query creator type must not be null\");\n\t\tAssert.notNull(repositoryQueryType, \"RepositoryQueryType type type must not be null\");\n\n\t\tsuper.afterPropertiesSet();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/repository/support/QuerydslKeyValuePredicateExecutor.java",
    "content": "/*\n * Copyright 2021-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.repository.support;\n\nimport static org.springframework.data.keyvalue.repository.support.KeyValueQuerydslUtils.*;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\nimport java.util.stream.Stream;\nimport org.jspecify.annotations.Nullable;\nimport org.springframework.dao.IncorrectResultSizeDataAccessException;\nimport org.springframework.data.convert.DtoInstantiatingConverter;\nimport org.springframework.data.domain.Page;\nimport org.springframework.data.domain.PageImpl;\nimport org.springframework.data.domain.Pageable;\nimport org.springframework.data.domain.Sort;\nimport org.springframework.data.keyvalue.core.IterableConverter;\nimport org.springframework.data.keyvalue.core.KeyValueOperations;\nimport org.springframework.data.mapping.PersistentEntity;\nimport org.springframework.data.mapping.PersistentProperty;\nimport org.springframework.data.mapping.context.MappingContext;\nimport org.springframework.data.mapping.model.EntityInstantiators;\nimport org.springframework.data.projection.ProjectionFactory;\nimport org.springframework.data.projection.SpelAwareProxyProjectionFactory;\nimport org.springframework.data.querydsl.EntityPathResolver;\nimport org.springframework.data.querydsl.ListQuerydslPredicateExecutor;\nimport org.springframework.data.querydsl.QuerydslPredicateExecutor;\nimport org.springframework.data.querydsl.SimpleEntityPathResolver;\nimport org.springframework.data.repository.core.EntityInformation;\nimport org.springframework.data.repository.query.FluentQuery;\nimport org.springframework.util.Assert;\n\nimport com.querydsl.collections.AbstractCollQuery;\nimport com.querydsl.collections.CollQuery;\nimport com.querydsl.core.NonUniqueResultException;\nimport com.querydsl.core.QueryResults;\nimport com.querydsl.core.types.EntityPath;\nimport com.querydsl.core.types.OrderSpecifier;\nimport com.querydsl.core.types.Predicate;\nimport com.querydsl.core.types.dsl.PathBuilder;\n\n/**\n * {@link QuerydslPredicateExecutor} capable of applying {@link Predicate}s using {@link CollQuery}.\n *\n * @author Mark Paluch\n * @since 2.6\n */\npublic class QuerydslKeyValuePredicateExecutor<T> implements ListQuerydslPredicateExecutor<T> {\n\n\tprivate static final EntityPathResolver DEFAULT_ENTITY_PATH_RESOLVER = SimpleEntityPathResolver.INSTANCE;\n\n\tprivate final MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> context;\n\tprivate final PathBuilder<T> builder;\n\tprivate final Supplier<List<T>> findAll;\n\tprivate final EntityInformation<T, ?> entityInformation;\n\tprivate final ProjectionFactory projectionFactory;\n\tprivate final EntityInstantiators entityInstantiators = new EntityInstantiators();\n\n\t/**\n\t * Creates a new {@link QuerydslKeyValuePredicateExecutor} for the given {@link EntityInformation}.\n\t *\n\t * @param entityInformation must not be {@literal null}.\n\t * @param operations must not be {@literal null}.\n\t */\n\tpublic QuerydslKeyValuePredicateExecutor(EntityInformation<T, ?> entityInformation, KeyValueOperations operations) {\n\t\tthis(entityInformation, new SpelAwareProxyProjectionFactory(), operations, DEFAULT_ENTITY_PATH_RESOLVER);\n\t}\n\n\t/**\n\t * Creates a new {@link QuerydslKeyValuePredicateExecutor} for the given {@link EntityInformation}, and\n\t * {@link EntityPathResolver}.\n\t *\n\t * @param entityInformation must not be {@literal null}.\n\t * @param projectionFactory must not be {@literal null}.\n\t * @param operations must not be {@literal null}.\n\t * @param resolver must not be {@literal null}.\n\t */\n\tpublic QuerydslKeyValuePredicateExecutor(EntityInformation<T, ?> entityInformation,\n\t\t\tProjectionFactory projectionFactory, KeyValueOperations operations,\n\t\t\tEntityPathResolver resolver) {\n\n\t\tAssert.notNull(entityInformation, \"EntityInformation must not be null\");\n\t\tAssert.notNull(projectionFactory, \"ProjectionFactory must not be null\");\n\t\tAssert.notNull(operations, \"KeyValueOperations must not be null\");\n\t\tAssert.notNull(resolver, \"EntityPathResolver must not be null\");\n\n\t\tthis.projectionFactory = projectionFactory;\n\t\tthis.context = operations.getMappingContext();\n\t\tEntityPath<T> path = resolver.createPath(entityInformation.getJavaType());\n\t\tthis.builder = new PathBuilder<>(path.getType(), path.getMetadata());\n\t\tthis.entityInformation = entityInformation;\n\t\tfindAll = () -> IterableConverter.toList(operations.findAll(entityInformation.getJavaType()));\n\t}\n\n\t@Override\n\tpublic Optional<T> findOne(Predicate predicate) {\n\n\t\tAssert.notNull(predicate, \"Predicate must not be null\");\n\n\t\ttry {\n\t\t\treturn Optional.ofNullable(prepareQuery(predicate).fetchOne());\n\t\t} catch (NonUniqueResultException o_O) {\n\t\t\tthrow new IncorrectResultSizeDataAccessException(\"Expected one or no result but found more than one\", 1, o_O);\n\t\t}\n\t}\n\n\t@Override\n\tpublic List<T> findAll(Predicate predicate) {\n\n\t\tAssert.notNull(predicate, \"Predicate must not be null\");\n\n\t\treturn prepareQuery(predicate).fetchResults().getResults();\n\t}\n\n\t@Override\n\tpublic List<T> findAll(Predicate predicate, OrderSpecifier<?>... orders) {\n\n\t\tAssert.notNull(predicate, \"Predicate must not be null\");\n\t\tAssert.notNull(orders, \"OrderSpecifiers must not be null\");\n\n\t\tAbstractCollQuery<T, ?> query = prepareQuery(predicate);\n\t\tquery.orderBy(orders);\n\n\t\treturn query.fetchResults().getResults();\n\t}\n\n\t@Override\n\tpublic List<T> findAll(Predicate predicate, Sort sort) {\n\n\t\tAssert.notNull(predicate, \"Predicate must not be null\");\n\t\tAssert.notNull(sort, \"Sort must not be null\");\n\n\t\treturn findAll(predicate, toOrderSpecifier(sort, builder));\n\t}\n\n\t@Override\n\tpublic Page<T> findAll(Predicate predicate, Pageable pageable) {\n\n\t\tAssert.notNull(predicate, \"Predicate must not be null\");\n\t\tAssert.notNull(pageable, \"Pageable must not be null\");\n\n\t\tAbstractCollQuery<T, ?> query = prepareQuery(predicate);\n\n\t\tif (pageable.isPaged() || pageable.getSort().isSorted()) {\n\n\t\t\tquery.offset(pageable.getOffset());\n\t\t\tquery.limit(pageable.getPageSize());\n\n\t\t\tif (pageable.getSort().isSorted()) {\n\t\t\t\tquery.orderBy(toOrderSpecifier(pageable.getSort(), builder));\n\t\t\t}\n\t\t}\n\n\t\treturn new PageImpl<>(query.fetchResults().getResults(), pageable, count(predicate));\n\t}\n\n\t@Override\n\tpublic List<T> findAll(OrderSpecifier<?>... orders) {\n\n\t\tAssert.notNull(orders, \"OrderSpecifiers must not be null\");\n\n\t\tif (orders.length == 0) {\n\t\t\treturn findAll.get();\n\t\t}\n\n\t\tAbstractCollQuery<T, ?> query = prepareQuery(null);\n\t\tquery.orderBy(orders);\n\n\t\treturn query.fetchResults().getResults();\n\t}\n\n\t@Override\n\tpublic long count(Predicate predicate) {\n\n\t\tAssert.notNull(predicate, \"Predicate must not be null\");\n\n\t\treturn prepareQuery(predicate).fetchCount();\n\t}\n\n\t@Override\n\tpublic boolean exists(Predicate predicate) {\n\n\t\tAssert.notNull(predicate, \"Predicate must not be null\");\n\n\t\treturn count(predicate) > 0;\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <S extends T, R> R findBy(Predicate predicate,\n\t\t\tFunction<FluentQuery.FetchableFluentQuery<S>, R> queryFunction) {\n\n\t\tAssert.notNull(predicate, \"Predicate must not be null\");\n\t\tAssert.notNull(queryFunction, \"Query function must not be null\");\n\n\t\treturn queryFunction.apply(new FluentQuerydsl<>(predicate, (Class<S>) entityInformation.getJavaType()));\n\t}\n\n\t/**\n\t * Creates executable query for given {@link Predicate}.\n\t *\n\t * @param predicate\n\t * @return\n\t */\n\tprotected AbstractCollQuery<T, ?> prepareQuery(@Nullable Predicate predicate) {\n\n\t\tCollQuery<T> query = new CollQuery<>();\n\t\tquery.from(builder, findAll.get());\n\n\t\treturn predicate != null ? query.where(predicate) : query;\n\t}\n\n\t/**\n\t * {@link org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery} using Querydsl\n\t * {@link Predicate}.\n\t *\n\t * @author Mark Paluch\n\t * @since 2.6\n\t */\n\tclass FluentQuerydsl<R> implements FluentQuery.FetchableFluentQuery<R> {\n\n\t\tprivate final Predicate predicate;\n\t\tprivate final Sort sort;\n\t\tprivate final Class<?> entityType;\n\t\tprivate final Class<R> resultType;\n\t\tprivate final List<String> fieldsToInclude;\n\n\t\tFluentQuerydsl(Predicate predicate, Class<R> resultType) {\n\t\t\tthis(predicate, Sort.unsorted(), resultType, resultType, Collections.emptyList());\n\t\t}\n\n\t\tpublic FluentQuerydsl(Predicate predicate, Sort sort, Class<?> entityType, Class<R> resultType,\n\t\t\t\tList<String> fieldsToInclude) {\n\t\t\tthis.predicate = predicate;\n\t\t\tthis.sort = sort;\n\t\t\tthis.entityType = entityType;\n\t\t\tthis.resultType = resultType;\n\t\t\tthis.fieldsToInclude = fieldsToInclude;\n\t\t}\n\n\t\t@Override\n\t\tpublic FluentQuery.FetchableFluentQuery<R> sortBy(Sort sort) {\n\n\t\t\tAssert.notNull(sort, \"Sort must not be null\");\n\n\t\t\treturn new FluentQuerydsl<>(predicate, sort, entityType, resultType, fieldsToInclude);\n\t\t}\n\n\t\t@Override\n\t\tpublic <NR> FluentQuery.FetchableFluentQuery<NR> as(Class<NR> projection) {\n\n\t\t\tAssert.notNull(projection, \"Projection target type must not be null\");\n\n\t\t\treturn new FluentQuerydsl<>(predicate, sort, entityType, projection, fieldsToInclude);\n\t\t}\n\n\t\tpublic FluentQuery.FetchableFluentQuery<R> project(Collection<String> properties) {\n\n\t\t\tAssert.notNull(properties, \"Projection properties must not be null\");\n\n\t\t\treturn new FluentQuerydsl<>(predicate, sort, entityType, resultType, new ArrayList<>(properties));\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable R oneValue() {\n\n\t\t\tList<T> results = createQuery().limit(2).fetch();\n\n\t\t\tif (results.isEmpty()) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tif (results.size() > 1) {\n\t\t\t\tthrow new IncorrectResultSizeDataAccessException(1);\n\t\t\t}\n\n\t\t\tT one = results.get(0);\n\t\t\treturn getConversionFunction().apply(one);\n\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable R firstValue() {\n\n\t\t\tList<T> results = createQuery().limit(1).fetch();\n\n\t\t\tif (results.isEmpty()) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tT one = results.get(0);\n\t\t\treturn getConversionFunction().apply(one);\n\t\t}\n\n\t\t@Override\n\t\tpublic List<R> all() {\n\n\t\t\tList<T> results = createQuery().fetch();\n\n\t\t\treturn mapResults(results);\n\t\t}\n\n\t\t@Override\n\t\tpublic Page<R> page(Pageable pageable) {\n\n\t\t\tAssert.notNull(pageable, \"Pageable must not be null\");\n\n\t\t\tAbstractCollQuery<T, ?> query = createQuery();\n\n\t\t\tif (pageable.isPaged() || pageable.getSort().isSorted()) {\n\n\t\t\t\tquery.offset(pageable.getOffset());\n\t\t\t\tquery.limit(pageable.getPageSize());\n\n\t\t\t\tif (pageable.getSort().isSorted()) {\n\t\t\t\t\tquery.orderBy(toOrderSpecifier(pageable.getSort(), builder));\n\t\t\t\t}\n\t\t\t}\n\t\t\tQueryResults<T> results = query.limit(pageable.getPageSize()).offset(pageable.getOffset()).fetchResults();\n\n\t\t\treturn new PageImpl<>(mapResults(results.getResults()), pageable, results.getTotal());\n\t\t}\n\n\t\t@Override\n\t\tpublic Stream<R> stream() {\n\t\t\treturn createQuery().stream().map(getConversionFunction());\n\t\t}\n\n\t\t@Override\n\t\tpublic long count() {\n\t\t\treturn createQuery().fetchCount();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean exists() {\n\t\t\treturn count() > 0;\n\t\t}\n\n\t\tprivate AbstractCollQuery<T, ?> createQuery() {\n\n\t\t\tAbstractCollQuery<T, ?> query = prepareQuery(predicate);\n\n\t\t\tif (sort.isSorted()) {\n\t\t\t\tquery.orderBy(toOrderSpecifier(sort, builder));\n\t\t\t}\n\t\t\treturn query;\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprivate List<R> mapResults(List<T> results) {\n\n\t\t\tif (entityType == resultType) {\n\t\t\t\treturn (List<R>) results;\n\t\t\t}\n\n\t\t\tList<R> mapped = new ArrayList<>(results.size());\n\n\t\t\tFunction<Object, R> converter = getConversionFunction();\n\t\t\tfor (T result : results) {\n\t\t\t\tmapped.add(converter.apply(result));\n\t\t\t}\n\n\t\t\treturn mapped;\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprivate <P> Function<Object, P> getConversionFunction(Class<?> inputType, Class<P> targetType) {\n\n\t\t\tif (targetType.isAssignableFrom(inputType)) {\n\t\t\t\treturn (Function<Object, P>) Function.identity();\n\t\t\t}\n\n\t\t\tif (targetType.isInterface()) {\n\t\t\t\treturn o -> projectionFactory.createProjection(targetType, o);\n\t\t\t}\n\n\t\t\tDtoInstantiatingConverter converter = new DtoInstantiatingConverter(targetType, context, entityInstantiators);\n\n\t\t\treturn o -> (P) converter.convert(o);\n\t\t}\n\n\t\tprivate Function<Object, R> getConversionFunction() {\n\t\t\treturn getConversionFunction(entityType, resultType);\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/repository/support/QuerydslKeyValueRepository.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.repository.support;\n\nimport java.util.Optional;\nimport java.util.function.Function;\n\nimport org.springframework.data.domain.Page;\nimport org.springframework.data.domain.Pageable;\nimport org.springframework.data.domain.Sort;\nimport org.springframework.data.keyvalue.core.KeyValueOperations;\nimport org.springframework.data.keyvalue.repository.KeyValueRepository;\nimport org.springframework.data.projection.SpelAwareProxyProjectionFactory;\nimport org.springframework.data.querydsl.EntityPathResolver;\nimport org.springframework.data.querydsl.QuerydslPredicateExecutor;\nimport org.springframework.data.querydsl.SimpleEntityPathResolver;\nimport org.springframework.data.repository.core.EntityInformation;\nimport org.springframework.data.repository.query.FluentQuery;\nimport org.springframework.util.Assert;\n\nimport com.querydsl.collections.CollQuery;\nimport com.querydsl.core.types.OrderSpecifier;\nimport com.querydsl.core.types.Predicate;\n\n/**\n * {@link KeyValueRepository} implementation capable of executing {@link Predicate}s using {@link CollQuery}.\n *\n * @author Christoph Strobl\n * @author Oliver Gierke\n * @author Thomas Darimont\n * @author Mark Paluch\n * @param <T> the domain type to manage\n * @param <ID> the identifier type of the domain type\n * @deprecated since 2.6, use {@link QuerydslKeyValuePredicateExecutor} instead.\n */\n@Deprecated\npublic class QuerydslKeyValueRepository<T, ID> extends SimpleKeyValueRepository<T, ID>\n\t\timplements QuerydslPredicateExecutor<T> {\n\n\tprivate final QuerydslKeyValuePredicateExecutor<T> executor;\n\n\t/**\n\t * Creates a new {@link QuerydslKeyValueRepository} for the given {@link EntityInformation} and\n\t * {@link KeyValueOperations}.\n\t *\n\t * @param entityInformation must not be {@literal null}.\n\t * @param operations must not be {@literal null}.\n\t */\n\tpublic QuerydslKeyValueRepository(EntityInformation<T, ID> entityInformation, KeyValueOperations operations) {\n\t\tthis(entityInformation, operations, SimpleEntityPathResolver.INSTANCE);\n\t}\n\n\t/**\n\t * Creates a new {@link QuerydslKeyValueRepository} for the given {@link EntityInformation},\n\t * {@link KeyValueOperations} and {@link EntityPathResolver}.\n\t *\n\t * @param entityInformation must not be {@literal null}.\n\t * @param operations must not be {@literal null}.\n\t * @param resolver must not be {@literal null}.\n\t */\n\tpublic QuerydslKeyValueRepository(EntityInformation<T, ID> entityInformation, KeyValueOperations operations,\n\t\t\tEntityPathResolver resolver) {\n\n\t\tsuper(entityInformation, operations);\n\n\t\tAssert.notNull(resolver, \"EntityPathResolver must not be null\");\n\n\t\tthis.executor = new QuerydslKeyValuePredicateExecutor<>(entityInformation, new SpelAwareProxyProjectionFactory(),\n\t\t\t\toperations, resolver);\n\t}\n\n\t@Override\n\tpublic Optional<T> findOne(Predicate predicate) {\n\t\treturn executor.findOne(predicate);\n\t}\n\n\t@Override\n\tpublic Iterable<T> findAll(Predicate predicate) {\n\t\treturn executor.findAll(predicate);\n\t}\n\n\t@Override\n\tpublic Iterable<T> findAll(Predicate predicate, OrderSpecifier<?>... orders) {\n\t\treturn executor.findAll(predicate, orders);\n\t}\n\n\t@Override\n\tpublic Iterable<T> findAll(Predicate predicate, Sort sort) {\n\t\treturn executor.findAll(predicate, sort);\n\t}\n\n\t@Override\n\tpublic Page<T> findAll(Predicate predicate, Pageable pageable) {\n\t\treturn executor.findAll(predicate, pageable);\n\t}\n\n\t@Override\n\tpublic Iterable<T> findAll(OrderSpecifier<?>... orders) {\n\t\treturn executor.findAll(orders);\n\t}\n\n\t@Override\n\tpublic long count(Predicate predicate) {\n\t\treturn executor.count(predicate);\n\t}\n\n\t@Override\n\tpublic boolean exists(Predicate predicate) {\n\t\treturn executor.exists(predicate);\n\t}\n\n\t@Override\n\tpublic <S extends T, R> R findBy(Predicate predicate,\n\t\t\tFunction<FluentQuery.FetchableFluentQuery<S>, R> queryFunction) {\n\t\treturn executor.findBy(predicate, queryFunction);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/repository/support/SimpleKeyValueRepository.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.repository.support;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.springframework.data.domain.Page;\nimport org.springframework.data.domain.PageImpl;\nimport org.springframework.data.domain.Pageable;\nimport org.springframework.data.domain.Sort;\nimport org.springframework.data.keyvalue.core.IterableConverter;\nimport org.springframework.data.keyvalue.core.KeyValueOperations;\nimport org.springframework.data.keyvalue.repository.KeyValueRepository;\nimport org.springframework.data.repository.core.EntityInformation;\nimport org.springframework.util.Assert;\n\n/**\n * Simple {@link KeyValueRepository} implementation.\n *\n * @author Christoph Strobl\n * @author Oliver Gierke\n * @author Mark Paluch\n * @author Eugene Nikiforov\n * @param <T>\n * @param <ID>\n */\npublic class SimpleKeyValueRepository<T, ID> implements KeyValueRepository<T, ID> {\n\n\tprivate final KeyValueOperations operations;\n\tprivate final EntityInformation<T, ID> entityInformation;\n\n\t/**\n\t * Creates a new {@link SimpleKeyValueRepository} for the given {@link EntityInformation} and\n\t * {@link KeyValueOperations}.\n\t *\n\t * @param metadata must not be {@literal null}.\n\t * @param operations must not be {@literal null}.\n\t */\n\tpublic SimpleKeyValueRepository(EntityInformation<T, ID> metadata, KeyValueOperations operations) {\n\n\t\tAssert.notNull(metadata, \"EntityInformation must not be null\");\n\t\tAssert.notNull(operations, \"KeyValueOperations must not be null\");\n\n\t\tthis.entityInformation = metadata;\n\t\tthis.operations = operations;\n\t}\n\n\t// -------------------------------------------------------------------------\n\t// Methods from CrudRepository\n\t// -------------------------------------------------------------------------\n\n\t@Override\n\tpublic <S extends T> S save(S entity) {\n\n\t\tAssert.notNull(entity, \"Entity must not be null\");\n\n\t\tif (entityInformation.isNew(entity)) {\n\t\t\treturn operations.insert(entity);\n\t\t}\n\n\t\treturn operations.update(entityInformation.getRequiredId(entity), entity);\n\t}\n\n\t@Override\n\tpublic <S extends T> List<S> saveAll(Iterable<S> entities) {\n\n\t\tAssert.notNull(entities, \"The given Iterable of entities must not be null\");\n\n\t\tList<S> saved = new ArrayList<>();\n\n\t\tfor (S entity : entities) {\n\t\t\tsaved.add(save(entity));\n\t\t}\n\n\t\treturn saved;\n\t}\n\n\t@Override\n\tpublic Optional<T> findById(ID id) {\n\n\t\tAssert.notNull(id, \"The given id must not be null\");\n\n\t\treturn operations.findById(id, entityInformation.getJavaType());\n\t}\n\n\t@Override\n\tpublic boolean existsById(ID id) {\n\t\treturn findById(id).isPresent();\n\t}\n\n\t@Override\n\tpublic List<T> findAll() {\n\t\treturn IterableConverter.toList(operations.findAll(entityInformation.getJavaType()));\n\t}\n\n\t@Override\n\tpublic List<T> findAllById(Iterable<ID> ids) {\n\n\t\tAssert.notNull(ids, \"The given Iterable of id's must not be null\");\n\n\t\tList<T> result = new ArrayList<>();\n\n\t\tids.forEach(id -> findById(id).ifPresent(result::add));\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic long count() {\n\t\treturn operations.count(entityInformation.getJavaType());\n\t}\n\n\t@Override\n\tpublic void deleteById(ID id) {\n\n\t\tAssert.notNull(id, \"The given id must not be null\");\n\n\t\toperations.delete(id, entityInformation.getJavaType());\n\t}\n\n\t@Override\n\tpublic void delete(T entity) {\n\n\t\tAssert.notNull(entity, \"The given entity must not be null\");\n\n\t\toperations.delete(entity);\n\t}\n\n\t@Override\n\tpublic void deleteAllById(Iterable<? extends ID> ids) {\n\n\t\tAssert.notNull(ids, \"The given Iterable of Ids must not be null\");\n\n\t\tids.forEach(this::deleteById);\n\t}\n\n\t@Override\n\tpublic void deleteAll(Iterable<? extends T> entities) {\n\n\t\tAssert.notNull(entities, \"The given Iterable of entities must not be null\");\n\n\t\tentities.forEach(this::delete);\n\t}\n\n\t@Override\n\tpublic void deleteAll() {\n\t\toperations.delete(entityInformation.getJavaType());\n\t}\n\n\t// -------------------------------------------------------------------------\n\t// Methods from PagingAndSortingRepository\n\t// -------------------------------------------------------------------------\n\n\t@Override\n\tpublic List<T> findAll(Sort sort) {\n\n\t\tAssert.notNull(sort, \"Sort must not be null\");\n\n\t\treturn IterableConverter.toList(operations.findAll(sort, entityInformation.getJavaType()));\n\t}\n\n\t@Override\n\tpublic Page<T> findAll(Pageable pageable) {\n\n\t\tAssert.notNull(pageable, \"Pageable must not be null\");\n\n\t\tif (pageable.isUnpaged()) {\n\t\t\tList<T> result = findAll();\n\t\t\treturn new PageImpl<>(result, Pageable.unpaged(), result.size());\n\t\t}\n\n\t\tIterable<T> content = operations.findInRange(pageable.getOffset(), pageable.getPageSize(), pageable.getSort(),\n\t\t\t\tentityInformation.getJavaType());\n\n\t\treturn new PageImpl<>(IterableConverter.toList(content), pageable,\n\t\t\t\tthis.operations.count(entityInformation.getJavaType()));\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/keyvalue/repository/support/package-info.java",
    "content": "/**\n * Support infrastructure for query derivation of key/value specific repositories.\n */\n@org.jspecify.annotations.NullMarked\npackage org.springframework.data.keyvalue.repository.support;\n"
  },
  {
    "path": "src/main/java/org/springframework/data/map/KeySpaceStore.java",
    "content": "/*\n * Copyright 2025-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.map;\n\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * Strategy interface to obtain a map for a given key space. Implementations should be thread-safe when intended for use\n * with multiple threads (both, the store itself and the used keystore maps).\n * <p>\n * Can be used to plug in keystore creation or implementation strategies (for example, Map-based implementations such as\n * MapDB or Infinispan) through a consolidated interface. A keyspace store represents a map of maps or a database with\n * multiple collections and can use any kind of map per keyspace.\n * <p>\n * For example, a {@link ConcurrentHashMap} can be used as keystore map type to allow concurrent access to keyspaces\n * using:\n *\n * <pre class=\"code\">\n * KeyspaceStore store = KeyspaceStore.create();\n * </pre>\n *\n * Custom map types (or instances of these) can be used as well using the provided factory methods:\n *\n * <pre class=\"code\">\n * KeyspaceStore store = KeyspaceStore.of(LinkedHashMap.class);\n *\n * Map<String, Map<Object, Object>> backingMap = …;\n * KeyspaceStore store = KeyspaceStore.of(backingMap);\n * </pre>\n *\n * @since 4.0\n */\npublic interface KeySpaceStore {\n\n\t/**\n\t * Return the map associated with given keyspace. Implementations can return an empty map if the keyspace does not\n\t * exist yet or a reference to the map that represents an existing keyspace holding keys and values for the requested\n\t * keyspace.\n\t *\n\t * @param keyspace name of the keyspace to obtain the map for, must not be {@literal null}.\n\t * @return the map associated with the given keyspace, never {@literal null}.\n\t */\n\tMap<Object, Object> getKeySpace(String keyspace);\n\n\t/**\n\t * Clear all keyspaces. Access to {@link #getKeySpace(String)} will return an empty map for each keyspace after this\n\t * method call. It is not required to clear each keyspace individually but it makes sense to do so to free up memory.\n\t */\n\tvoid clear();\n\n\t/**\n\t * Create a new {@link KeySpaceStore} using {@link ConcurrentHashMap} as backing map type for each keyspace map.\n\t *\n\t * @return a new and empty {@link KeySpaceStore}.\n\t */\n\tstatic KeySpaceStore create() {\n\t\treturn MapKeySpaceStore.create();\n\t}\n\n\t/**\n\t * Create new {@link KeySpaceStore} using given map type for each keyspace map.\n\t *\n\t * @param mapType map type to use.\n\t * @return the new {@link KeySpaceStore} object.\n\t */\n\t@SuppressWarnings(\"rawtypes\")\n\tstatic KeySpaceStore of(Class<? extends Map> mapType) {\n\t\treturn MapKeySpaceStore.of(mapType);\n\t}\n\n\t/**\n\t * Create new {@link KeySpaceStore} using given map as backing store. Determines the map type from the given map.\n\t *\n\t * @param store map of maps.\n\t * @return the new {@link KeySpaceStore} object for the given {@code store}.\n\t */\n\tstatic KeySpaceStore of(Map<String, Map<Object, Object>> store) {\n\t\treturn MapKeySpaceStore.of(store);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/map/MapKeySpaceStore.java",
    "content": "/*\n * Copyright 2025-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.map;\n\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.springframework.core.CollectionFactory;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Keyspace store that uses a map of maps to store keyspace data. The outer map holds the keyspaces and the inner maps\n * hold the actual keys and values.\n *\n * @param store reference to the map of maps holding the keyspace data.\n * @param keySpaceMapType map type to be used for each keyspace map.\n * @param initialCapacity initial keyspace map capacity to optimize allocations.\n * @since 4.0\n */\n@SuppressWarnings(\"rawtypes\")\nrecord MapKeySpaceStore(Map<String, Map<Object, Object>> store, Class<? extends Map> keySpaceMapType,\n\t\tint initialCapacity) implements KeySpaceStore {\n\n\tpublic static final int DEFAULT_INITIAL_CAPACITY = 1000;\n\n\t/**\n\t * Create a new {@link KeySpaceStore} using {@link ConcurrentHashMap} as backing map type for each keyspace map.\n\t *\n\t * @return a new and empty {@link KeySpaceStore}.\n\t */\n\tpublic static KeySpaceStore create() {\n\t\treturn new MapKeySpaceStore(new ConcurrentHashMap<>(100), ConcurrentHashMap.class, DEFAULT_INITIAL_CAPACITY);\n\t}\n\n\t/**\n\t * Create new {@link KeySpaceStore} using given map type for each keyspace map.\n\t *\n\t * @param mapType map type to use.\n\t * @return the new {@link KeySpaceStore} object.\n\t */\n\tpublic static KeySpaceStore of(Class<? extends Map> mapType) {\n\n\t\tAssert.notNull(mapType, \"Store map type must not be null\");\n\n\t\treturn of(CollectionFactory.createMap(mapType, 100));\n\t}\n\n\t/**\n\t * Create new {@link KeySpaceStore} using given map as backing store. Determines the map type from the given map.\n\t *\n\t * @param store map of maps.\n\t * @return the new {@link KeySpaceStore} object for the given {@code store}.\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic static KeySpaceStore of(Map<String, Map<Object, Object>> store) {\n\n\t\tAssert.notNull(store, \"Store map must not be null\");\n\n\t\tClass<? extends Map<?, ?>> userClass = (Class<? extends Map<?, ?>>) ClassUtils.getUserClass(store);\n\t\treturn new MapKeySpaceStore(store, userClass, DEFAULT_INITIAL_CAPACITY);\n\t}\n\n\t@Override\n\tpublic Map<Object, Object> getKeySpace(String keyspace) {\n\t\treturn store.computeIfAbsent(keyspace, k -> CollectionFactory.createMap(keySpaceMapType, initialCapacity));\n\t}\n\n\t@Override\n\tpublic void clear() {\n\n\t\tstore.values().forEach(Map::clear);\n\t\tstore.clear();\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/map/MapKeyValueAdapter.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.map;\n\nimport java.util.Collection;\nimport java.util.Comparator;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.data.keyvalue.core.AbstractKeyValueAdapter;\nimport org.springframework.data.keyvalue.core.ForwardingCloseableIterator;\nimport org.springframework.data.keyvalue.core.KeyValueAdapter;\nimport org.springframework.data.keyvalue.core.PredicateQueryEngine;\nimport org.springframework.data.keyvalue.core.QueryEngine;\nimport org.springframework.data.keyvalue.core.SortAccessor;\nimport org.springframework.data.util.CloseableIterator;\nimport org.springframework.util.Assert;\n\n/**\n * {@link KeyValueAdapter} implementation for {@link Map}.\n *\n * @author Christoph Strobl\n * @author Derek Cochran\n * @author Marcel Overdijk\n */\npublic class MapKeyValueAdapter extends AbstractKeyValueAdapter {\n\n\tprivate final KeySpaceStore store;\n\n\t/**\n\t * Create new {@link MapKeyValueAdapter} using {@link ConcurrentHashMap} as backing store type.\n\t */\n\tpublic MapKeyValueAdapter() {\n\t\tthis(MapKeySpaceStore.create());\n\t}\n\n\t/**\n\t * Create new {@link MapKeyValueAdapter} using the given query engine.\n\t *\n\t * @param engine the query engine.\n\t * @since 2.4\n\t */\n\tpublic MapKeyValueAdapter(QueryEngine<? extends KeyValueAdapter, ?, ?> engine) {\n\t\tthis(MapKeySpaceStore.create(), engine);\n\t}\n\n\t/**\n\t * Creates a new {@link MapKeyValueAdapter} using the given {@link Map} as backing store.\n\t *\n\t * @param mapType must not be {@literal null}.\n\t */\n\t@SuppressWarnings(\"rawtypes\")\n\tpublic MapKeyValueAdapter(Class<? extends Map> mapType) {\n\t\tthis(MapKeySpaceStore.of(mapType));\n\t}\n\n\t/**\n\t * Creates a new {@link MapKeyValueAdapter} using the given {@link Map} as backing store.\n\t *\n\t * @param mapType must not be {@literal null}.\n\t * @param sortAccessor accessor granting access to sorting implementation\n\t * @since 3.1.10\n\t */\n\t@SuppressWarnings(\"rawtypes\")\n\tpublic MapKeyValueAdapter(Class<? extends Map> mapType, SortAccessor<Comparator<?>> sortAccessor) {\n\t\tthis(MapKeySpaceStore.of(mapType), new PredicateQueryEngine(sortAccessor));\n\t}\n\n\t/**\n\t * Creates a new {@link MapKeyValueAdapter} using the given {@link Map} as backing store and query engine.\n\t *\n\t * @param mapType must not be {@literal null}.\n\t * @param engine the query engine.\n\t * @since 2.4\n\t */\n\t@SuppressWarnings(\"rawtypes\")\n\tpublic MapKeyValueAdapter(Class<? extends Map> mapType, QueryEngine<? extends KeyValueAdapter, ?, ?> engine) {\n\t\tthis(MapKeySpaceStore.of(mapType), engine);\n\t}\n\n\t/**\n\t * Create new instance of {@link MapKeyValueAdapter} using given dataStore for persistence.\n\t *\n\t * @param store must not be {@literal null}.\n\t */\n\tpublic MapKeyValueAdapter(Map<String, Map<Object, Object>> store) {\n\t\tthis(MapKeySpaceStore.of(store));\n\t}\n\n\t/**\n\t * Create new instance of {@link MapKeyValueAdapter} using given dataStore for persistence and query engine.\n\t *\n\t * @param store must not be {@literal null}.\n\t * @param engine the query engine.\n\t * @since 2.4\n\t */\n\tpublic MapKeyValueAdapter(Map<String, Map<Object, Object>> store, QueryEngine<? extends KeyValueAdapter, ?, ?> engine) {\n\t\tthis(MapKeySpaceStore.of(store), engine);\n\t}\n\n\t/**\n\t * Create new instance of {@link MapKeyValueAdapter} using given dataStore for persistence.\n\t *\n\t * @param store must not be {@literal null}.\n\t */\n\tpublic MapKeyValueAdapter(KeySpaceStore store) {\n\t\tthis(store, new PredicateQueryEngine());\n\t}\n\n\t/**\n\t * Creates a new {@link MapKeyValueAdapter} with the given store and type to be used when creating key spaces and\n\t * query engine.\n\t *\n\t * @param store must not be {@literal null}.\n\t * @param engine the query engine.\n\t */\n\tpublic MapKeyValueAdapter(KeySpaceStore store, @Nullable QueryEngine<? extends KeyValueAdapter, ?, ?> engine) {\n\n\t\tsuper(engine);\n\n\t\tAssert.notNull(store, \"KeyspaceStore must not be null\");\n\t\tthis.store = store;\n\t}\n\n\t@Override\n\tpublic @Nullable Object put(Object id, Object item, String keyspace) {\n\n\t\tAssert.notNull(id, \"Cannot add item with null id\");\n\t\tAssert.notNull(keyspace, \"Cannot add item for null collection\");\n\n\t\treturn getKeySpaceMap(keyspace).put(id, item);\n\t}\n\n\t@Override\n\tpublic boolean contains(Object id, String keyspace) {\n\t\treturn get(id, keyspace) != null;\n\t}\n\n\t@Override\n\tpublic long count(String keyspace) {\n\t\treturn getKeySpaceMap(keyspace).size();\n\t}\n\n\t@Override\n\tpublic @Nullable Object get(Object id, String keyspace) {\n\n\t\tAssert.notNull(id, \"Cannot get item with null id\");\n\t\treturn getKeySpaceMap(keyspace).get(id);\n\t}\n\n\t@Override\n\tpublic @Nullable Object delete(Object id, String keyspace) {\n\n\t\tAssert.notNull(id, \"Cannot delete item with null id\");\n\t\treturn getKeySpaceMap(keyspace).remove(id);\n\t}\n\n\t@Override\n\tpublic Collection<Object> getAllOf(String keyspace) {\n\t\treturn getKeySpaceMap(keyspace).values();\n\t}\n\n\t@Override\n\tpublic CloseableIterator<Entry<Object, Object>> entries(String keyspace) {\n\t\treturn new ForwardingCloseableIterator<>(getKeySpaceMap(keyspace).entrySet().iterator());\n\t}\n\n\t@Override\n\tpublic void deleteAllOf(String keyspace) {\n\t\tgetKeySpaceMap(keyspace).clear();\n\t}\n\n\t@Override\n\tpublic void clear() {\n\t\tstore.clear();\n\t}\n\n\t@Override\n\tpublic void destroy() throws Exception {\n\t\tclear();\n\t}\n\n\t/**\n\t * Get map associated with given key space.\n\t *\n\t * @param keyspace must not be {@literal null}.\n\t * @return\n\t */\n\tprotected Map<Object, Object> getKeySpaceMap(String keyspace) {\n\n\t\tAssert.notNull(keyspace, \"Collection must not be null for lookup\");\n\t\treturn store.getKeySpace(keyspace);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/map/package-info.java",
    "content": "/**\n * Repository implementation backed by generic {@link java.util.Map} instances.\n */\n@org.jspecify.annotations.NullMarked\npackage org.springframework.data.map;\n"
  },
  {
    "path": "src/main/java/org/springframework/data/map/repository/config/EnableMapRepositories.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.map.repository.config;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.beans.factory.support.BeanNameGenerator;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.ComponentScan.Filter;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.core.annotation.AliasFor;\nimport org.springframework.data.keyvalue.core.KeyValueOperations;\nimport org.springframework.data.keyvalue.core.KeyValueTemplate;\nimport org.springframework.data.keyvalue.core.QueryEngineFactory;\nimport org.springframework.data.keyvalue.core.SortAccessor;\nimport org.springframework.data.keyvalue.repository.config.QueryCreatorType;\nimport org.springframework.data.keyvalue.repository.query.PredicateQueryCreator;\nimport org.springframework.data.keyvalue.repository.support.KeyValueRepositoryFactoryBean;\nimport org.springframework.data.repository.config.DefaultRepositoryBaseClass;\nimport org.springframework.data.repository.query.QueryLookupStrategy;\nimport org.springframework.data.repository.query.QueryLookupStrategy.Key;\nimport org.springframework.data.repository.query.parser.AbstractQueryCreator;\n\n/**\n * Annotation to activate Map repositories. If no base package is configured through either {@link #value()},\n * {@link #basePackages()} or {@link #basePackageClasses()} it will trigger scanning of the package of annotated class.\n *\n * @author Christoph Strobl\n * @author Oliver Gierke\n * @author Mark Paluch\n */\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@Import(MapRepositoriesRegistrar.class)\n@QueryCreatorType(PredicateQueryCreator.class)\npublic @interface EnableMapRepositories {\n\n\t/**\n\t * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation declarations e.g.:\n\t * {@code @EnableJpaRepositories(\"org.my.pkg\")} instead of {@code @EnableJpaRepositories(basePackages=\"org.my.pkg\")}.\n\t */\n\tString[] value() default {};\n\n\t/**\n\t * Base packages to scan for annotated components. {@link #value()} is an alias for (and mutually exclusive with) this\n\t * attribute. Use {@link #basePackageClasses()} for a type-safe alternative to String-based package names.\n\t */\n\tString[] basePackages() default {};\n\n\t/**\n\t * Type-safe alternative to {@link #basePackages()} for specifying the packages to scan for annotated components. The\n\t * package of each class specified will be scanned. Consider creating a special no-op marker class or interface in\n\t * each package that serves no purpose other than being referenced by this attribute.\n\t */\n\tClass<?>[] basePackageClasses() default {};\n\n\t/**\n\t * Specifies which types are not eligible for component scanning.\n\t */\n\tFilter[] excludeFilters() default {};\n\n\t/**\n\t * Specifies which types are eligible for component scanning. Further narrows the set of candidate components from\n\t * everything in {@link #basePackages()} to everything in the base packages that matches the given filter or filters.\n\t */\n\tFilter[] includeFilters() default {};\n\n\t/**\n\t * Returns the postfix to be used when looking up custom repository implementations. Defaults to {@literal Impl}. So\n\t * for a repository named {@code PersonRepository} the corresponding implementation class will be looked up scanning\n\t * for {@code PersonRepositoryImpl}.\n\t *\n\t * @return\n\t */\n\tString repositoryImplementationPostfix() default \"Impl\";\n\n\t/**\n\t * Configures the location of where to find the Spring Data named queries properties file.\n\t *\n\t * @return\n\t */\n\tString namedQueriesLocation() default \"\";\n\n\t/**\n\t * Returns the key of the {@link QueryLookupStrategy} to be used for lookup queries for query methods. Defaults to\n\t * {@link Key#CREATE_IF_NOT_FOUND}.\n\t *\n\t * @return\n\t */\n\tKey queryLookupStrategy() default Key.CREATE_IF_NOT_FOUND;\n\n\t/**\n\t * Returns the {@link FactoryBean} class to be used for each repository instance. Defaults to\n\t * {@link KeyValueRepositoryFactoryBean}.\n\t *\n\t * @return\n\t */\n\tClass<?> repositoryFactoryBeanClass() default KeyValueRepositoryFactoryBean.class;\n\n\t/**\n\t * Configure the repository base class to be used to create repository proxies for this particular configuration.\n\t *\n\t * @return\n\t */\n\tClass<?> repositoryBaseClass() default DefaultRepositoryBaseClass.class;\n\n\t/**\n\t * Configure a specific {@link BeanNameGenerator} to be used when creating the repository beans.\n\t * @return the {@link BeanNameGenerator} to be used or the base {@link BeanNameGenerator} interface to indicate context default.\n\t * @since 3.4\n\t */\n\tClass<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;\n\n\t/**\n\t * Configures the name of the {@link KeyValueOperations} bean to be used with the repositories detected.\n\t *\n\t * @return\n\t */\n\tString keyValueTemplateRef() default \"mapKeyValueTemplate\";\n\n\t/**\n\t * Configures whether nested repository-interfaces (e.g. defined as inner classes) should be discovered by the\n\t * repositories infrastructure.\n\t */\n\tboolean considerNestedRepositories() default false;\n\n\t/**\n\t * Configures the {@link Map} structure used for data storage. Defaults to {@link ConcurrentHashMap}. Will be ignored\n\t * in case an explicit bean for the {@link KeyValueTemplate} is available in the {@link ApplicationContext} or\n\t * {@link #keySpaceStoreRef()} is configured.\n\t *\n\t * @see #keyValueTemplateRef()\n\t */\n\t@SuppressWarnings(\"rawtypes\")\n\tClass<? extends Map> mapType() default ConcurrentHashMap.class;\n\n\t/**\n\t * Configures the name to a {@link org.springframework.data.map.KeySpaceStore} bean to be used as database for all\n\t * keyspaces.\n\t *\n\t * @since 4.0\n\t * @see org.springframework.data.map.KeySpaceStore\n\t */\n\tString keySpaceStoreRef() default \"\";\n\n\t/**\n\t * Configures the {@link QueryEngineFactory} to create the QueryEngine. When both, the query engine and sort accessors\n\t * are configured, the query engine is instantiated using the configured sort accessor.\n\t *\n\t * @return {@link QueryEngineFactory} to configure the QueryEngine.\n\t * @since 3.3.1\n\t */\n\tClass<? extends QueryEngineFactory> queryEngineFactory() default QueryEngineFactory.class;\n\n\t/**\n\t * Configures the {@code QueryCreator} to create Part-Tree queries. The QueryCreator must create queries supported by\n\t * the underlying {@code QueryEngine}.\n\t *\n\t * @return {@link AbstractQueryCreator}\n\t * @since 3.3.1\n\t */\n\t@AliasFor(annotation = QueryCreatorType.class, value = \"value\")\n\tClass<? extends AbstractQueryCreator<?, ?>> queryCreator() default PredicateQueryCreator.class;\n\n\t/**\n\t * Configures the {@link SortAccessor accessor} for sorting results.\n\t *\n\t * @return the configured {@link SortAccessor}.\n\t * @since 3.1.10\n\t */\n\tClass<? extends SortAccessor> sortAccessor() default SortAccessor.class;\n\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/map/repository/config/MapRepositoriesRegistrar.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.map.repository.config;\n\nimport java.lang.annotation.Annotation;\n\nimport org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport;\nimport org.springframework.data.repository.config.RepositoryConfigurationExtension;\n\n/**\n * Map specific {@link RepositoryBeanDefinitionRegistrarSupport} implementation.\n *\n * @author Christoph Strobl\n */\npublic class MapRepositoriesRegistrar extends RepositoryBeanDefinitionRegistrarSupport {\n\n\t@Override\n\tprotected Class<? extends Annotation> getAnnotation() {\n\t\treturn EnableMapRepositories.class;\n\t}\n\n\t@Override\n\tprotected RepositoryConfigurationExtension getExtension() {\n\t\treturn new MapRepositoryConfigurationExtension();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/map/repository/config/MapRepositoryConfigurationExtension.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.map.repository.config;\n\nimport java.lang.reflect.Constructor;\nimport java.util.Optional;\nimport java.util.function.Predicate;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.support.AbstractBeanDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.data.config.ParsingUtils;\nimport org.springframework.data.keyvalue.core.KeyValueTemplate;\nimport org.springframework.data.keyvalue.core.QueryEngine;\nimport org.springframework.data.keyvalue.core.QueryEngineFactory;\nimport org.springframework.data.keyvalue.core.SortAccessor;\nimport org.springframework.data.keyvalue.repository.config.KeyValueRepositoryConfigurationExtension;\nimport org.springframework.data.map.KeySpaceStore;\nimport org.springframework.data.map.MapKeyValueAdapter;\nimport org.springframework.data.repository.config.RepositoryConfigurationExtension;\nimport org.springframework.data.repository.config.RepositoryConfigurationSource;\nimport org.springframework.util.ClassUtils;\n\n/**\n * {@link RepositoryConfigurationExtension} for Map-based repositories.\n *\n * @author Christoph Strobl\n * @author Mark Paluch\n */\n@SuppressWarnings(\"unchecked\")\npublic class MapRepositoryConfigurationExtension extends KeyValueRepositoryConfigurationExtension {\n\n\t@Override\n\tpublic String getModuleName() {\n\t\treturn \"Map\";\n\t}\n\n\t@Override\n\tprotected String getModulePrefix() {\n\t\treturn \"map\";\n\t}\n\n\t@Override\n\tprotected String getDefaultKeyValueTemplateRef() {\n\t\treturn \"mapKeyValueTemplate\";\n\t}\n\n\t@Override\n\tprotected AbstractBeanDefinition getDefaultKeyValueTemplateBeanDefinition(\n\t\t\tRepositoryConfigurationSource configurationSource) {\n\n\t\tBeanDefinitionBuilder adapterBuilder = BeanDefinitionBuilder.rootBeanDefinition(MapKeyValueAdapter.class);\n\t\tadapterBuilder.addConstructorArgValue(getKeySpaceStore(configurationSource));\n\n\t\tSortAccessor<?> sortAccessor = getSortAccessor(configurationSource);\n\t\tQueryEngine<?, ?, ?> queryEngine = getQueryEngine(sortAccessor, configurationSource);\n\n\t\tif (queryEngine != null) {\n\t\t\tadapterBuilder.addConstructorArgValue(queryEngine);\n\t\t} else if (sortAccessor != null) {\n\t\t\tadapterBuilder.addConstructorArgValue(sortAccessor);\n\t\t}\n\n\t\tBeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(KeyValueTemplate.class);\n\t\tbuilder\n\t\t\t\t.addConstructorArgValue(ParsingUtils.getSourceBeanDefinition(adapterBuilder, configurationSource.getSource()));\n\t\tbuilder.setRole(BeanDefinition.ROLE_SUPPORT);\n\n\t\treturn ParsingUtils.getSourceBeanDefinition(builder, configurationSource.getSource());\n\t}\n\n\tprivate static Object getKeySpaceStore(RepositoryConfigurationSource source) {\n\n\t\tOptional<String> keySpaceStoreRef = source.getAttribute(\"keySpaceStoreRef\", String.class);\n\n\t\treturn keySpaceStoreRef.map(beanName -> new RuntimeBeanReference(beanName, KeySpaceStore.class)) //\n\t\t\t\t.map(Object.class::cast) //\n\t\t\t\t.orElseGet(() -> source.getRequiredAttribute(\"mapType\", Class.class));\n\t}\n\n\tprivate static @Nullable SortAccessor<?> getSortAccessor(RepositoryConfigurationSource source) {\n\n\t\tClass<? extends SortAccessor<?>> sortAccessorType = getClassAttribute(source, \"sortAccessor\");\n\n\t\tif (sortAccessorType == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn BeanUtils.instantiateClass(sortAccessorType);\n\t}\n\n\tprivate static @Nullable QueryEngine<?, ?, ?> getQueryEngine(@Nullable SortAccessor<?> sortAccessor,\n\t\t\tRepositoryConfigurationSource source) {\n\n\t\tClass<? extends QueryEngineFactory> queryEngineFactoryType = getClassAttribute(source, \"queryEngineFactory\");\n\n\t\tif (queryEngineFactoryType == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (sortAccessor != null) {\n\t\t\tConstructor<? extends QueryEngineFactory> constructor = ClassUtils\n\t\t\t\t\t.getConstructorIfAvailable(queryEngineFactoryType, SortAccessor.class);\n\t\t\tif (constructor != null) {\n\t\t\t\treturn BeanUtils.instantiateClass(constructor, sortAccessor).create();\n\t\t\t}\n\t\t}\n\n\t\treturn BeanUtils.instantiateClass(queryEngineFactoryType).create();\n\t}\n\n\tprivate static <T> @Nullable Class<T> getClassAttribute(RepositoryConfigurationSource source, String attributeName) {\n\t\treturn source.getAttribute(attributeName, Class.class).filter(Predicate.not(Class::isInterface)).orElse(null);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/org/springframework/data/map/repository/config/package-info.java",
    "content": "/**\n * Support infrastructure for the configuration of {@link java.util.Map} repositories.\n */\n@org.jspecify.annotations.NullMarked\npackage org.springframework.data.map.repository.config;\n"
  },
  {
    "path": "src/main/resources/META-INF/spring/aot.factories",
    "content": "org.springframework.aot.hint.RuntimeHintsRegistrar=\\\n\torg.springframework.data.keyvalue.aot.KeyValueRuntimeHints\n"
  },
  {
    "path": "src/main/resources/META-INF/spring.factories",
    "content": "org.springframework.data.repository.core.support.RepositoryFactorySupport=org.springframework.data.keyvalue.repository.support.KeyValueRepositoryFactory\n"
  },
  {
    "path": "src/main/resources/license.txt",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        https://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   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\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       https://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.\n\n=======================================================================\n\nSPRING FRAMEWORK ${version} SUBCOMPONENTS:\n\nSpring Framework ${version} includes a number of subcomponents\nwith separate copyright notices and license terms. The product that\nincludes this file does not necessarily use all the open source\nsubcomponents referred to below. Your use of the source\ncode for these subcomponents is subject to the terms and\nconditions of the following licenses.\n\n\n>>> ASM 4.0 (org.ow2.asm:asm:4.0, org.ow2.asm:asm-commons:4.0):\n\nCopyright (c) 2000-2011 INRIA, France Telecom\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n\n1. Redistributions of source code must retain the above copyright\n   notice, this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright\n   notice, this list of conditions and the following disclaimer in the\n   documentation and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holders nor the names of its\n   contributors may be used to endorse or promote products derived from\n   this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\nTHE POSSIBILITY OF SUCH DAMAGE.\n\nCopyright (c) 1999-2009, OW2 Consortium <https://www.ow2.org/>\n\n\n>>> CGLIB 3.0 (cglib:cglib:3.0):\n\nPer the LICENSE file in the CGLIB JAR distribution downloaded from\nhttps://sourceforge.net/projects/cglib/files/cglib3/3.0/cglib-3.0.jar/download,\nCGLIB 3.0 is licensed under the Apache License, version 2.0, the text of which\nis included above.\n\n\n=======================================================================\n\nTo the extent any open source subcomponents are licensed under the EPL and/or\nother similar licenses that require the source code and/or modifications to\nsource code to be made available (as would be noted above), you may obtain a\ncopy of the source code corresponding to the binaries for such open source\ncomponents and modifications thereto, if any, (the \"Source Files\"), by\ndownloading the Source Files from https://www.springsource.org/download, or by\nsending a request, with your name and address to:\n\n    VMware, Inc., 3401 Hillview Avenue\n    Palo Alto, CA 94304\n    United States of America\n\nor email info@vmware.com.  All such requests should clearly specify:\n\n    OPEN SOURCE FILES REQUEST\n    Attention General Counsel\n\nVMware shall mail a copy of the Source Files to you on a CD or equivalent\nphysical medium. This offer to obtain a copy of the Source Files is valid for\nthree years from the date you acquired this Software product."
  },
  {
    "path": "src/main/resources/notice.txt",
    "content": "Spring Data KeyValue 4.1 RC1 (2026.0.0)\nCopyright (c) 2015-2019 Pivotal Software, Inc.\n\nThis product is licensed to you under the Apache License, Version 2.0\n(the \"License\"). You may not use this product except in compliance with\nthe License.\n\nThis product may include a number of subcomponents with separate\ncopyright notices and license terms. Your use of the source code for\nthese subcomponents is subject to the terms and conditions of the\nsubcomponent's license, as noted in the license.txt file.\n\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/CustomKeySpaceAnnotationWithAliasFor.java",
    "content": "/*\n * Copyright 2016-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.core.annotation.AliasFor;\nimport org.springframework.data.annotation.Persistent;\nimport org.springframework.data.keyvalue.annotation.KeySpace;\n\n/**\n * Custom composed {@link Persistent} annotation using {@link AliasFor} on name attribute.\n *\n * @author Christoph Strobl\n */\n@Persistent\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ ElementType.TYPE })\n@KeySpace\npublic @interface CustomKeySpaceAnnotationWithAliasFor {\n\n\t@AliasFor(annotation = KeySpace.class, attribute = \"value\")\n\tString name() default \"\";\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/Person.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue;\n\nimport java.net.URL;\n\nimport org.springframework.data.annotation.Id;\n\nimport com.querydsl.core.annotations.QueryEntity;\n\n/**\n * @author Christoph Strobl\n * @author Mark Paluch\n */\n@QueryEntity\npublic class Person {\n\n\tprivate @Id String id;\n\tprivate String firstname;\n\tprivate int age;\n\tprivate URL homepage;\n\n\tpublic Person(String firstname, int age) {\n\t\tsuper();\n\t\tthis.firstname = firstname;\n\t\tthis.age = age;\n\t}\n\n\tpublic String getId() {\n\t\treturn this.id;\n\t}\n\n\tpublic String getFirstname() {\n\t\treturn this.firstname;\n\t}\n\n\tpublic int getAge() {\n\t\treturn this.age;\n\t}\n\n\tpublic void setId(String id) {\n\t\tthis.id = id;\n\t}\n\n\tpublic void setFirstname(String firstname) {\n\t\tthis.firstname = firstname;\n\t}\n\n\tpublic void setAge(int age) {\n\t\tthis.age = age;\n\t}\n\n\tpublic URL getHomepage() {\n\t\treturn homepage;\n\t}\n\n\tpublic void setHomepage(URL homepage) {\n\t\tthis.homepage = homepage;\n\t}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/SubclassOfTypeWithCustomComposedKeySpaceAnnotation.java",
    "content": "/*\n * Copyright 2015-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue;\n\nimport org.springframework.data.keyvalue.annotation.KeySpace;\n\n/**\n * Class that inherits its {@link KeySpace} from a super class annotated with a custom {@link CustomKeySpaceAnnotation}\n * annotation.\n *\n * @author Christoph Strobl\n */\npublic class SubclassOfTypeWithCustomComposedKeySpaceAnnotation\n\t\textends TypeWithCustomComposedKeySpaceAnnotationUsingAliasFor {\n\n\tpublic SubclassOfTypeWithCustomComposedKeySpaceAnnotation(String name) {\n\t\tsuper(name);\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/TypeWithCustomComposedKeySpaceAnnotationUsingAliasFor.java",
    "content": "/*\n * Copyright 2016-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue;\n\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.annotation.Persistent;\n\n/**\n * A {@link Persistent} type with {@link CustomKeySpaceAnnotationWithAliasFor}.\n *\n * @author Christoph Strobl\n * @author Mark Paluch\n */\n@CustomKeySpaceAnnotationWithAliasFor(name = \"aliased\")\npublic class TypeWithCustomComposedKeySpaceAnnotationUsingAliasFor {\n\n\t@Id String id;\n\tString name;\n\n\tpublic TypeWithCustomComposedKeySpaceAnnotationUsingAliasFor(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic String getId() {\n\t\treturn this.id;\n\t}\n\n\tpublic String getName() {\n\t\treturn this.name;\n\t}\n\n\tpublic void setId(String id) {\n\t\tthis.id = id;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/TypeWithDirectKeySpaceAnnotation.java",
    "content": "/*\n * Copyright 2015-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue;\n\nimport org.springframework.data.annotation.Persistent;\nimport org.springframework.data.keyvalue.annotation.KeySpace;\n\n/**\n * A {@link Persistent} type with explict {@link KeySpace}.\n *\n * @author Christoph Strobl\n */\n@KeySpace(\"rhaegar\")\npublic class TypeWithDirectKeySpaceAnnotation {\n\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/TypeWithInhteritedPersistentAnnotationNotHavingKeySpace.java",
    "content": "/*\n * Copyright 2015-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue;\n\nimport org.springframework.data.annotation.Persistent;\nimport org.springframework.data.annotation.TypeAlias;\nimport org.springframework.data.keyvalue.annotation.KeySpace;\n\n/**\n * A type inheriting {@link Persistent} from {@link TypeAlias} not having a {@link KeySpace} defined.\n *\n * @author Christoph Strobl\n */\n@TypeAlias(\"foo\")\npublic class TypeWithInhteritedPersistentAnnotationNotHavingKeySpace {\n\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/TypeWithPersistentAnnotationNotHavingKeySpace.java",
    "content": "/*\n * Copyright 2015-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue;\n\nimport org.springframework.data.annotation.Persistent;\nimport org.springframework.data.keyvalue.annotation.KeySpace;\n\n/**\n * A {@link Persistent} class without a defined {@link KeySpace}.\n *\n * @author Christoph Strobl\n */\n@Persistent\npublic class TypeWithPersistentAnnotationNotHavingKeySpace {\n\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/core/DefaultIdentifierGeneratorUnitTests.java",
    "content": "/*\n * Copyright 2016-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport static org.assertj.core.api.Assertions.*;\n\nimport java.util.Date;\nimport java.util.UUID;\n\nimport org.junit.jupiter.api.Test;\nimport org.springframework.dao.InvalidDataAccessApiUsageException;\nimport org.springframework.data.core.TypeInformation;\n\n/**\n * @author Christoph Strobl\n */\nclass DefaultIdentifierGeneratorUnitTests {\n\n\tprivate DefaultIdentifierGenerator generator = DefaultIdentifierGenerator.INSTANCE;\n\n\t@Test\n\tvoid shouldThrowExceptionForUnsupportedType() {\n\t\tassertThatExceptionOfType(InvalidDataAccessApiUsageException.class)\n\t\t\t\t.isThrownBy(() -> generator.generateIdentifierOfType(TypeInformation.of(Date.class)));\n\t}\n\n\t@Test // DATAKV-136\n\tvoid shouldGenerateUUIDValueCorrectly() {\n\n\t\tObject value = generator.generateIdentifierOfType(TypeInformation.of(UUID.class));\n\n\t\tassertThat(value).isNotNull().isInstanceOf(UUID.class);\n\t}\n\n\t@Test // DATAKV-136\n\tvoid shouldGenerateStringValueCorrectly() {\n\n\t\tObject value = generator.generateIdentifierOfType(TypeInformation.of(String.class));\n\n\t\tassertThat(value).isNotNull().isInstanceOf(String.class);\n\t}\n\n\t@Test // DATAKV-136\n\tvoid shouldGenerateLongValueCorrectly() {\n\n\t\tObject value = generator.generateIdentifierOfType(TypeInformation.of(Long.class));\n\n\t\tassertThat(value).isNotNull().isInstanceOf(Long.class);\n\t}\n\n\t@Test // DATAKV-136\n\tvoid shouldGenerateIntValueCorrectly() {\n\n\t\tObject value = generator.generateIdentifierOfType(TypeInformation.of(Integer.class));\n\n\t\tassertThat(value).isNotNull().isInstanceOf(Integer.class);\n\t}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/core/ForwardingCloseableIteratorUnitTests.java",
    "content": "/*\n * Copyright 2015-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport static org.assertj.core.api.Assertions.*;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.NoSuchElementException;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.data.util.CloseableIterator;\n\n/**\n * @author Christoph Strobl\n * @author Thomas Darimont\n * @author Oliver Gierke\n */\n@ExtendWith(MockitoExtension.class)\nclass ForwardingCloseableIteratorUnitTests<K, V> {\n\n\t@Mock Iterator<Entry<K, V>> iteratorMock;\n\t@Mock Runnable closeActionMock;\n\n\t@Test // DATAKV-99\n\tvoid hasNextShouldDelegateToWrappedIterator() {\n\n\t\twhen(iteratorMock.hasNext()).thenReturn(true);\n\n\t\tCloseableIterator<Entry<K, V>> iterator = new ForwardingCloseableIterator<>(iteratorMock);\n\n\t\ttry {\n\t\t\tassertThat(iterator.hasNext()).isTrue();\n\t\t\tverify(iteratorMock, times(1)).hasNext();\n\t\t} finally {\n\t\t\titerator.close();\n\t\t}\n\t}\n\n\t@Test // DATAKV-99\n\t@SuppressWarnings(\"unchecked\")\n\tvoid nextShouldDelegateToWrappedIterator() {\n\n\t\twhen(iteratorMock.next()).thenReturn((Entry<K, V>) mock(Map.Entry.class));\n\n\t\tCloseableIterator<Entry<K, V>> iterator = new ForwardingCloseableIterator<>(iteratorMock);\n\n\t\ttry {\n\t\t\tassertThat(iterator.next()).isNotNull();\n\t\t\tverify(iteratorMock, times(1)).next();\n\t\t} finally {\n\t\t\titerator.close();\n\t\t}\n\t}\n\n\t@Test // DATAKV-99\n\tvoid nextShouldThrowErrorWhenWrappedIteratorHasNoMoreElements() {\n\n\t\twhen(iteratorMock.next()).thenThrow(new NoSuchElementException());\n\n\t\tCloseableIterator<Entry<K, V>> iterator = new ForwardingCloseableIterator<>(iteratorMock);\n\n\t\ttry {\n\t\t\tassertThatExceptionOfType(NoSuchElementException.class).isThrownBy(iterator::next);\n\t\t} finally {\n\t\t\titerator.close();\n\t\t}\n\t}\n\n\t@Test // DATAKV-99\n\tvoid closeShouldDoNothingByDefault() {\n\n\t\tnew ForwardingCloseableIterator<>(iteratorMock).close();\n\n\t\tverifyNoInteractions(iteratorMock);\n\t}\n\n\t@Test // DATAKV-99\n\tvoid closeShouldInvokeConfiguredCloseAction() {\n\n\t\tnew ForwardingCloseableIterator<>(iteratorMock, closeActionMock).close();\n\n\t\tverify(closeActionMock, times(1)).run();\n\t}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/core/IterableConverterUnitTests.java",
    "content": "/*\n * Copyright 2015-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport static org.assertj.core.api.Assertions.*;\nimport static org.springframework.data.keyvalue.core.IterableConverter.*;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * @author Christoph Strobl\n * @author Mark Paluch\n */\nclass IterableConverterUnitTests {\n\n\t@Test // DATAKV-101\n\tvoid toListShouldReturnEmptyListWhenSourceEmpty() {\n\t\tassertThat(toList(Collections.emptySet())).isEmpty();\n\t}\n\n\t@Test // DATAKV-101\n\tvoid toListShouldReturnSameObjectWhenSourceIsAlreadyListType() {\n\n\t\tList<String> source = new ArrayList<>();\n\n\t\tassertThat(toList(source)).isSameAs(source);\n\t}\n\n\t@Test // DATAKV-101\n\tvoid toListShouldReturnListWhenSourceIsNonListType() {\n\n\t\tSet<String> source = new HashSet<>();\n\t\tsource.add(\"tyrion\");\n\n\t\tassertThat(toList(source)).isInstanceOf(List.class);\n\t}\n\n\t@Test // DATAKV-101\n\tvoid toListShouldHoldValuesInOrderOfSource() {\n\n\t\tSet<String> source = new LinkedHashSet<>();\n\t\tsource.add(\"tyrion\");\n\t\tsource.add(\"jaime\");\n\n\t\tassertThat(toList(source)).containsExactly(source.toArray(new String[2]));\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/core/KeyValuePersistenceExceptionTranslatorUnitTests.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport static org.assertj.core.api.Assertions.*;\n\nimport java.util.NoSuchElementException;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.beans.factory.NoSuchBeanDefinitionException;\nimport org.springframework.dao.DataRetrievalFailureException;\n\n/**\n * @author Christoph Strobl\n */\nclass KeyValuePersistenceExceptionTranslatorUnitTests {\n\n\tprivate KeyValuePersistenceExceptionTranslator translator = new KeyValuePersistenceExceptionTranslator();\n\n\t@Test // DATACMNS-525\n\tvoid translateExeptionShouldReturnDataAccessExceptionWhenGivenOne() {\n\t\tassertThat(translator.translateExceptionIfPossible(new DataRetrievalFailureException(\"booh\")))\n\t\t\t\t.isInstanceOf(DataRetrievalFailureException.class);\n\t}\n\n\t@Test // DATACMNS-525, DATAKV-192\n\tvoid translateExeptionShouldReturnNullWhenGivenNull() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> assertThat(translator.translateExceptionIfPossible(null)).isNull());\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid translateExeptionShouldTranslateNoSuchElementExceptionToDataRetrievalFailureException() {\n\t\tassertThat(translator.translateExceptionIfPossible(new NoSuchElementException(\"\")))\n\t\t\t\t.isInstanceOf(DataRetrievalFailureException.class);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid translateExeptionShouldTranslateIndexOutOfBoundsExceptionToDataRetrievalFailureException() {\n\t\tassertThat(translator.translateExceptionIfPossible(new IndexOutOfBoundsException(\"\")))\n\t\t\t\t.isInstanceOf(DataRetrievalFailureException.class);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid translateExeptionShouldTranslateIllegalStateExceptionToDataRetrievalFailureException() {\n\t\tassertThat(translator.translateExceptionIfPossible(new IllegalStateException(\"\")))\n\t\t\t\t.isInstanceOf(DataRetrievalFailureException.class);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid translateExeptionShouldTranslateAnyJavaExceptionToUncategorizedKeyValueException() {\n\t\tassertThat(translator.translateExceptionIfPossible(new UnsupportedOperationException(\"\")))\n\t\t\t\t.isInstanceOf(UncategorizedKeyValueException.class);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid translateExeptionShouldReturnNullForNonJavaExceptions() {\n\t\tassertThat(translator.translateExceptionIfPossible(new NoSuchBeanDefinitionException(\"\"))).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/core/KeyValueTemplateTests.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport static org.assertj.core.api.Assertions.*;\n\nimport java.io.Serializable;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.List;\nimport java.util.function.Predicate;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.core.annotation.AliasFor;\nimport org.springframework.dao.DuplicateKeyException;\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.annotation.Persistent;\nimport org.springframework.data.keyvalue.annotation.KeySpace;\nimport org.springframework.data.keyvalue.core.query.KeyValueQuery;\nimport org.springframework.data.map.MapKeyValueAdapter;\n\n/**\n * @author Christoph Strobl\n * @author Oliver Gierke\n * @author Mark Paluch\n */\nclass KeyValueTemplateTests {\n\n\tprivate static final Foo FOO_ONE = new Foo(\"one\");\n\tprivate static final Foo FOO_TWO = new Foo(\"two\");\n\tprivate static final Foo FOO_THREE = new Foo(\"three\");\n\tprivate static final Bar BAR_ONE = new Bar(\"one\");\n\tprivate static final ClassWithTypeAlias ALIASED = new ClassWithTypeAlias(\"super\");\n\tprivate static final SubclassOfAliasedType SUBCLASS_OF_ALIASED = new SubclassOfAliasedType(\"sub\");\n\n\tprivate static final KeyValueQuery<Predicate<Foo>> STRING_QUERY = new KeyValueQuery<>((Predicate<Foo>) foo -> foo.getFoo().equals(\"two\"));\n\n\tprivate KeyValueTemplate operations;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tthis.operations = new KeyValueTemplate(new MapKeyValueAdapter());\n\t}\n\n\t@AfterEach\n\tvoid tearDown() throws Exception {\n\t\tthis.operations.destroy();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid insertShouldNotThorwErrorWhenExecutedHavingNonExistingIdAndNonNullValue() {\n\t\toperations.insert(\"1\", FOO_ONE);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid insertShouldThrowExceptionForNullId() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> operations.insert(null, FOO_ONE));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid insertShouldThrowExceptionForNullObject() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> operations.insert(\"some-id\", null));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid insertShouldThrowExecptionWhenObjectOfSameTypeAlreadyExists() {\n\n\t\toperations.insert(\"1\", FOO_ONE);\n\n\t\tassertThatExceptionOfType(DuplicateKeyException.class).isThrownBy(() -> operations.insert(\"1\", FOO_TWO));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid insertShouldWorkCorrectlyWhenObjectsOfDifferentTypesWithSameIdAreInserted() {\n\n\t\toperations.insert(\"1\", FOO_ONE);\n\t\toperations.insert(\"1\", BAR_ONE);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid createShouldReturnSameInstanceGenerateId() {\n\n\t\tClassWithStringId source = new ClassWithStringId();\n\t\tClassWithStringId target = operations.insert(source);\n\n\t\tassertThat(target).isSameAs(source);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid createShouldRespectExistingId() {\n\n\t\tClassWithStringId source = new ClassWithStringId();\n\t\tsource.id = \"one\";\n\n\t\toperations.insert(source);\n\n\t\tassertThat(operations.findById(\"one\", ClassWithStringId.class)).contains(source);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid findByIdShouldReturnObjectWithMatchingIdAndType() {\n\n\t\toperations.insert(\"1\", FOO_ONE);\n\t\tassertThat(operations.findById(\"1\", Foo.class)).contains(FOO_ONE);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid findByIdSouldReturnOptionalEmptyIfNoMatchingIdFound() {\n\n\t\toperations.insert(\"1\", FOO_ONE);\n\t\tassertThat(operations.findById(\"2\", Foo.class)).isEmpty();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid findByIdShouldReturnOptionalEmptyIfNoMatchingTypeFound() {\n\n\t\toperations.insert(\"1\", FOO_ONE);\n\t\tassertThat(operations.findById(\"1\", Bar.class)).isEmpty();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid findShouldExecuteQueryCorrectly() {\n\n\t\toperations.insert(\"1\", FOO_ONE);\n\t\toperations.insert(\"2\", FOO_TWO);\n\n\t\tList<Foo> result = (List<Foo>) operations.find(STRING_QUERY, Foo.class);\n\t\tassertThat(result).hasSize(1);\n\t\tassertThat(result.get(0)).isEqualTo(FOO_TWO);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid readShouldReturnEmptyCollectionIfOffsetOutOfRange() {\n\n\t\toperations.insert(\"1\", FOO_ONE);\n\t\toperations.insert(\"2\", FOO_TWO);\n\t\toperations.insert(\"3\", FOO_THREE);\n\n\t\tassertThat(operations.findInRange(5, 5, Foo.class)).isEmpty();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid updateShouldReplaceExistingObject() {\n\n\t\toperations.insert(\"1\", FOO_ONE);\n\t\toperations.update(\"1\", FOO_TWO);\n\t\tassertThat(operations.findById(\"1\", Foo.class)).contains(FOO_TWO);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid updateShouldRespectTypeInformation() {\n\n\t\toperations.insert(\"1\", FOO_ONE);\n\t\toperations.update(\"1\", BAR_ONE);\n\n\t\tassertThat(operations.findById(\"1\", Foo.class)).contains(FOO_ONE);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid deleteShouldRemoveObjectCorrectly() {\n\n\t\toperations.insert(\"1\", FOO_ONE);\n\t\toperations.delete(\"1\", Foo.class);\n\t\tassertThat(operations.findById(\"1\", Foo.class)).isEmpty();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid deleteReturnsNullWhenNotExisting() {\n\n\t\toperations.insert(\"1\", FOO_ONE);\n\t\tassertThat(operations.delete(\"2\", Foo.class)).isNull();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid deleteReturnsRemovedObject() {\n\n\t\toperations.insert(\"1\", FOO_ONE);\n\t\tassertThat(operations.delete(\"1\", Foo.class)).isEqualTo(FOO_ONE);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid deleteThrowsExceptionWhenIdCannotBeExctracted() {\n\t\tassertThatIllegalStateException().isThrownBy(() -> operations.delete(FOO_ONE));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid countShouldReturnZeroWhenNoElementsPresent() {\n\t\tassertThat(operations.count(Foo.class)).isEqualTo(0L);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid insertShouldRespectTypeAlias() {\n\n\t\toperations.insert(\"1\", ALIASED);\n\t\toperations.insert(\"2\", SUBCLASS_OF_ALIASED);\n\n\t\tassertThat((List) operations.findAll(ALIASED.getClass())).contains(ALIASED, SUBCLASS_OF_ALIASED);\n\t}\n\n\tstatic class Foo {\n\n\t\tString foo;\n\n\t\tpublic Foo(String foo) {\n\t\t\tthis.foo = foo;\n\t\t}\n\n\t\tpublic String getFoo() {\n\t\t\treturn this.foo;\n\t\t}\n\n\t\tpublic void setFoo(String foo) {\n\t\t\tthis.foo = foo;\n\t\t}\n\t}\n\n\tstatic class Bar {\n\n\t\tString bar;\n\n\t\tpublic Bar(String bar) {\n\t\t\tthis.bar = bar;\n\t\t}\n\n\t\tpublic String getBar() {\n\t\t\treturn this.bar;\n\t\t}\n\n\t\tpublic void setBar(String bar) {\n\t\t\tthis.bar = bar;\n\t\t}\n\t}\n\n\tstatic class ClassWithStringId implements Serializable {\n\n\t\tprivate static final long serialVersionUID = -7481030649267602830L;\n\t\t@Id String id;\n\t\tString value;\n\n\t\tpublic String getId() {\n\t\t\treturn id;\n\t\t}\n\n\t\tpublic void setId(String id) {\n\t\t\tthis.id = id;\n\t\t}\n\n\t\tpublic String getValue() {\n\t\t\treturn value;\n\t\t}\n\n\t\tpublic void setValue(String value) {\n\t\t\tthis.value = value;\n\t\t}\n\t}\n\n\t@ExplicitKeySpace(name = \"aliased\")\n\tstatic class ClassWithTypeAlias implements Serializable {\n\n\t\tprivate static final long serialVersionUID = -5921943364908784571L;\n\t\t@Id String id;\n\t\tString name;\n\n\t\tClassWithTypeAlias(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\n\t\tpublic String getId() {\n\t\t\treturn this.id;\n\t\t}\n\n\t\tpublic String getName() {\n\t\t\treturn this.name;\n\t\t}\n\n\t\tpublic void setId(String id) {\n\t\t\tthis.id = id;\n\t\t}\n\n\t\tpublic void setName(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\t}\n\n\tstatic class SubclassOfAliasedType extends ClassWithTypeAlias {\n\n\t\tprivate static final long serialVersionUID = -468809596668871479L;\n\n\t\tSubclassOfAliasedType(String name) {\n\t\t\tsuper(name);\n\t\t}\n\n\t}\n\n\t@KeySpace\n\t@Persistent\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target({ ElementType.TYPE })\n\t@interface ExplicitKeySpace {\n\n\t\t@AliasFor(annotation = KeySpace.class, value = \"value\")\n\t\tString name() default \"\";\n\n\t}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/core/KeyValueTemplateUnitTests.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport static org.assertj.core.api.Assertions.*;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.mockito.junit.jupiter.MockitoSettings;\nimport org.mockito.quality.Strictness;\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.dao.DuplicateKeyException;\nimport org.springframework.dao.InvalidDataAccessApiUsageException;\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.keyvalue.SubclassOfTypeWithCustomComposedKeySpaceAnnotation;\nimport org.springframework.data.keyvalue.TypeWithCustomComposedKeySpaceAnnotationUsingAliasFor;\nimport org.springframework.data.keyvalue.core.event.KeyValueEvent;\nimport org.springframework.data.keyvalue.core.event.KeyValueEvent.AfterDeleteEvent;\nimport org.springframework.data.keyvalue.core.event.KeyValueEvent.AfterDropKeySpaceEvent;\nimport org.springframework.data.keyvalue.core.event.KeyValueEvent.AfterGetEvent;\nimport org.springframework.data.keyvalue.core.event.KeyValueEvent.AfterInsertEvent;\nimport org.springframework.data.keyvalue.core.event.KeyValueEvent.AfterUpdateEvent;\nimport org.springframework.data.keyvalue.core.event.KeyValueEvent.BeforeDeleteEvent;\nimport org.springframework.data.keyvalue.core.event.KeyValueEvent.BeforeGetEvent;\nimport org.springframework.data.keyvalue.core.event.KeyValueEvent.BeforeInsertEvent;\nimport org.springframework.data.keyvalue.core.event.KeyValueEvent.BeforeUpdateEvent;\nimport org.springframework.data.keyvalue.core.query.KeyValueQuery;\n\n/**\n * @author Christoph Strobl\n * @author Thomas Darimont\n * @author Oliver Gierke\n * @author Mark Paluch\n */\n@ExtendWith(MockitoExtension.class)\n@MockitoSettings(strictness = Strictness.LENIENT)\nclass KeyValueTemplateUnitTests {\n\n\tprivate static final Foo FOO_ONE = new Foo(\"one\");\n\tprivate static final Foo FOO_TWO = new Foo(\"two\");\n\tprivate static final TypeWithCustomComposedKeySpaceAnnotationUsingAliasFor ALIASED_USING_ALIAS_FOR = new TypeWithCustomComposedKeySpaceAnnotationUsingAliasFor(\n\t\t\t\"super\");\n\tprivate static final SubclassOfTypeWithCustomComposedKeySpaceAnnotation SUBCLASS_OF_ALIASED_USING_ALIAS_FOR = new SubclassOfTypeWithCustomComposedKeySpaceAnnotation(\n\t\t\t\"sub\");\n\tprivate static final KeyValueQuery<String> STRING_QUERY = new KeyValueQuery<>(\"foo == 'two'\");\n\n\tprivate @Mock KeyValueAdapter adapterMock;\n\tprivate KeyValueTemplate template;\n\tprivate @Mock ApplicationEventPublisher publisherMock;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tthis.template = new KeyValueTemplate(adapterMock);\n\t\tthis.template.setApplicationEventPublisher(publisherMock);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid shouldThrowExceptionWhenCreatingNewTempateWithNullAdapter() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new KeyValueTemplate(null));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid shouldThrowExceptionWhenCreatingNewTempateWithNullMappingContext() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new KeyValueTemplate(adapterMock, null));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid insertShouldLookUpValuesBeforeInserting() {\n\n\t\ttemplate.insert(\"1\", FOO_ONE);\n\n\t\tverify(adapterMock, times(1)).contains(\"1\", Foo.class.getName());\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid insertShouldInsertUseClassNameAsDefaultKeyspace() {\n\n\t\ttemplate.insert(\"1\", FOO_ONE);\n\n\t\tverify(adapterMock, times(1)).put(\"1\", FOO_ONE, Foo.class.getName());\n\t}\n\n\t@Test // DATACMNS-225\n\tvoid insertShouldReturnInsertedObject() {\n\n\t\tClassWithStringId object = new ClassWithStringId();\n\n\t\tassertThat(template.insert(object)).isEqualTo(object);\n\t\tassertThat(template.insert(\"1\", object)).isEqualTo(object);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid insertShouldThrowExceptionWhenObectWithIdAlreadyExists() {\n\n\t\twhen(adapterMock.contains(anyString(), anyString())).thenReturn(true);\n\n\t\tassertThatExceptionOfType(DuplicateKeyException.class).isThrownBy(() -> template.insert(\"1\", FOO_ONE));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid insertShouldThrowExceptionForNullId() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> template.insert(null, FOO_ONE));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid insertShouldThrowExceptionForNullObject() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> template.insert(\"some-id\", null));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid insertShouldGenerateId() {\n\n\t\tClassWithStringId target = template.insert(new ClassWithStringId());\n\n\t\tassertThat(target.id).isNotNull();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid insertShouldThrowErrorWhenIdCannotBeResolved() {\n\t\tassertThatIllegalStateException().isThrownBy(() -> template.insert(FOO_ONE));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid insertShouldReturnSameInstanceGenerateId() {\n\n\t\tClassWithStringId source = new ClassWithStringId();\n\t\tClassWithStringId target = template.insert(source);\n\n\t\tassertThat(target).isSameAs(source);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid insertShouldRespectExistingId() {\n\n\t\tClassWithStringId source = new ClassWithStringId();\n\t\tsource.id = \"one\";\n\n\t\ttemplate.insert(source);\n\n\t\tverify(adapterMock, times(1)).put(\"one\", source, ClassWithStringId.class.getName());\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid findByIdShouldReturnOptionalEmptyWhenNoElementsPresent() {\n\t\tassertThat(template.findById(\"1\", Foo.class)).isEmpty();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid findByIdShouldReturnObjectWithMatchingIdAndType() {\n\n\t\ttemplate.findById(\"1\", Foo.class);\n\n\t\tverify(adapterMock, times(1)).get(\"1\", Foo.class.getName(), Foo.class);\n\t}\n\n\t@Test // DATACMNS-525, DATAKV-187\n\tvoid findByIdShouldThrowExceptionWhenGivenNullId() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> template.findById(null, Foo.class));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid findAllOfShouldReturnEntireCollection() {\n\n\t\ttemplate.findAll(Foo.class);\n\n\t\tverify(adapterMock, times(1)).getAllOf(Foo.class.getName(), Foo.class);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid findAllOfShouldThrowExceptionWhenGivenNullType() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> template.findAll(null));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid findShouldCallFindOnAdapterToResolveMatching() {\n\n\t\ttemplate.find(STRING_QUERY, Foo.class);\n\n\t\tverify(adapterMock, times(1)).find(STRING_QUERY, Foo.class.getName(), Foo.class);\n\t}\n\n\t@Test // DATACMNS-525\n\t@SuppressWarnings(\"rawtypes\")\n\tvoid findInRangeShouldRespectOffset() {\n\n\t\tArgumentCaptor<KeyValueQuery> captor = ArgumentCaptor.forClass(KeyValueQuery.class);\n\n\t\ttemplate.findInRange(1, 5, Foo.class);\n\n\t\tverify(adapterMock, times(1)).find(captor.capture(), eq(Foo.class.getName()), eq(Foo.class));\n\t\tassertThat(captor.getValue().getOffset()).isEqualTo(1L);\n\t\tassertThat(captor.getValue().getRows()).isEqualTo(5);\n\t\tassertThat(captor.getValue().getCriteria()).isNull();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid updateShouldReplaceExistingObject() {\n\n\t\ttemplate.update(\"1\", FOO_TWO);\n\n\t\tverify(adapterMock, times(1)).put(\"1\", FOO_TWO, Foo.class.getName());\n\t}\n\n\t@Test // DATAKV-225\n\tvoid updateShouldReturnUpdatedObject() {\n\n\t\tClassWithStringId object = new ClassWithStringId();\n\t\tobject.id = \"foo\";\n\n\t\tassertThat(template.update(object)).isEqualTo(object);\n\t\tassertThat(template.update(\"1\", object)).isEqualTo(object);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid updateShouldThrowExceptionWhenGivenNullId() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> template.update(null, FOO_ONE));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid updateShouldThrowExceptionWhenGivenNullObject() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> template.update(\"1\", null));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid updateShouldUseExtractedIdInformation() {\n\n\t\tClassWithStringId source = new ClassWithStringId();\n\t\tsource.id = \"some-id\";\n\n\t\ttemplate.update(source);\n\n\t\tverify(adapterMock, times(1)).put(source.id, source, ClassWithStringId.class.getName());\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid updateShouldThrowErrorWhenIdInformationCannotBeExtracted() {\n\t\tassertThatExceptionOfType(InvalidDataAccessApiUsageException.class).isThrownBy(() -> template.update(FOO_ONE));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid deleteShouldRemoveObjectCorrectly() {\n\n\t\ttemplate.delete(\"1\", Foo.class);\n\n\t\tverify(adapterMock, times(1)).delete(\"1\", Foo.class.getName(), Foo.class);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid deleteRemovesObjectUsingExtractedId() {\n\n\t\tClassWithStringId source = new ClassWithStringId();\n\t\tsource.id = \"some-id\";\n\n\t\ttemplate.delete(source);\n\n\t\tverify(adapterMock, times(1)).delete(\"some-id\", ClassWithStringId.class.getName(), ClassWithStringId.class);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid deleteThrowsExceptionWhenIdCannotBeExctracted() {\n\t\tassertThatIllegalStateException().isThrownBy(() -> template.delete(FOO_ONE));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid countShouldReturnZeroWhenNoElementsPresent() {\n\t\ttemplate.count(Foo.class);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid countShouldReturnCollectionSize() {\n\n\t\twhen(adapterMock.count(Foo.class.getName())).thenReturn(2L);\n\n\t\tassertThat(template.count(Foo.class)).isEqualTo(2L);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid countShouldThrowErrorOnNullType() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> template.count(null));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid insertShouldRespectTypeAlias() {\n\n\t\ttemplate.insert(\"1\", ALIASED_USING_ALIAS_FOR);\n\n\t\tverify(adapterMock, times(1)).put(\"1\", ALIASED_USING_ALIAS_FOR, \"aliased\");\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid insertShouldRespectTypeAliasOnSubClass() {\n\n\t\ttemplate.insert(\"1\", SUBCLASS_OF_ALIASED_USING_ALIAS_FOR);\n\n\t\tverify(adapterMock, times(1)).put(\"1\", SUBCLASS_OF_ALIASED_USING_ALIAS_FOR, \"aliased\");\n\t}\n\n\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\t@Test // DATACMNS-525\n\tvoid findAllOfShouldRespectTypeAliasAndFilterNonMatchingTypes() {\n\n\t\tCollection foo = Arrays.asList(ALIASED_USING_ALIAS_FOR, SUBCLASS_OF_ALIASED_USING_ALIAS_FOR);\n\t\twhen(adapterMock.getAllOf(\"aliased\", SUBCLASS_OF_ALIASED_USING_ALIAS_FOR.getClass())).thenReturn(foo);\n\n\t\tassertThat((Iterable) template.findAll(SUBCLASS_OF_ALIASED_USING_ALIAS_FOR.getClass()))\n\t\t\t\t.contains(SUBCLASS_OF_ALIASED_USING_ALIAS_FOR);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid insertSouldRespectTypeAliasAndFilterNonMatching() {\n\n\t\ttemplate.insert(\"1\", ALIASED_USING_ALIAS_FOR);\n\t\tassertThat(template.findById(\"1\", SUBCLASS_OF_ALIASED_USING_ALIAS_FOR.getClass())).isEmpty();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid setttingNullPersistenceExceptionTranslatorShouldThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> template.setExceptionTranslator(null));\n\t}\n\n\t@Test // DATAKV-91\n\tvoid shouldNotPublishEventWhenNoApplicationContextSet() {\n\n\t\ttemplate.setApplicationEventPublisher(null);\n\n\t\ttemplate.insert(\"1\", FOO_ONE);\n\n\t\tverifyNoInteractions(publisherMock);\n\t}\n\n\t@Test // DATAKV-104\n\tvoid shouldNotPublishEventsWhenEventsToPublishIsSetToNull() {\n\n\t\ttemplate.setEventTypesToPublish(null);\n\n\t\ttemplate.insert(\"1\", FOO_ONE);\n\n\t\tverifyNoInteractions(publisherMock);\n\t}\n\n\t@Test // DATAKV-104\n\n\tvoid shouldNotPublishEventsWhenEventsToPublishIsSetToEmptyList() {\n\n\t\ttemplate.setEventTypesToPublish(Collections.emptySet());\n\n\t\ttemplate.insert(\"1\", FOO_ONE);\n\n\t\tverifyNoInteractions(publisherMock);\n\t}\n\n\t@Test // DATAKV-104\n\tvoid shouldPublishEventsByDefault() {\n\n\t\ttemplate.insert(\"1\", FOO_ONE);\n\n\t\tverify(publisherMock, atLeastOnce()).publishEvent(any());\n\t}\n\n\t@Test // DATAKV-91, DATAKV-104\n\n\tvoid shouldNotPublishEventWhenNotExplicitlySetForPublication() {\n\n\t\tsetEventsToPublish(BeforeDeleteEvent.class);\n\n\t\ttemplate.insert(\"1\", FOO_ONE);\n\n\t\tverifyNoInteractions(publisherMock);\n\t}\n\n\t@Test // DATAKV-91, DATAKV-104, DATAKV-187\n\t@SuppressWarnings({ \"rawtypes\" })\n\tvoid shouldPublishBeforeInsertEventCorrectly() {\n\n\t\tsetEventsToPublish(BeforeInsertEvent.class);\n\n\t\ttemplate.insert(\"1\", FOO_ONE);\n\n\t\tArgumentCaptor<BeforeInsertEvent> captor = ArgumentCaptor.forClass(BeforeInsertEvent.class);\n\n\t\tverify(publisherMock, times(1)).publishEvent(captor.capture());\n\t\tverifyNoMoreInteractions(publisherMock);\n\n\t\tassertThat(captor.getValue().getKey()).isEqualTo(\"1\");\n\t\tassertThat(captor.getValue().getKeyspace()).isEqualTo(Foo.class.getName());\n\t\tassertThat(captor.getValue().getPayload()).isEqualTo(FOO_ONE);\n\t}\n\n\t@Test // DATAKV-91, DATAKV-104, DATAKV-187\n\t@SuppressWarnings({ \"rawtypes\" })\n\tvoid shouldPublishAfterInsertEventCorrectly() {\n\n\t\tsetEventsToPublish(AfterInsertEvent.class);\n\n\t\ttemplate.insert(\"1\", FOO_ONE);\n\n\t\tArgumentCaptor<AfterInsertEvent> captor = ArgumentCaptor.forClass(AfterInsertEvent.class);\n\n\t\tverify(publisherMock, times(1)).publishEvent(captor.capture());\n\t\tverifyNoMoreInteractions(publisherMock);\n\n\t\tassertThat(captor.getValue().getKey()).isEqualTo(\"1\");\n\t\tassertThat(captor.getValue().getKeyspace()).isEqualTo(Foo.class.getName());\n\t\tassertThat(captor.getValue().getPayload()).isEqualTo(FOO_ONE);\n\t}\n\n\t@Test // DATAKV-91, DATAKV-104, DATAKV-187\n\t@SuppressWarnings({ \"rawtypes\" })\n\tvoid shouldPublishBeforeUpdateEventCorrectly() {\n\n\t\tsetEventsToPublish(BeforeUpdateEvent.class);\n\n\t\ttemplate.update(\"1\", FOO_ONE);\n\n\t\tArgumentCaptor<BeforeUpdateEvent> captor = ArgumentCaptor.forClass(BeforeUpdateEvent.class);\n\n\t\tverify(publisherMock, times(1)).publishEvent(captor.capture());\n\t\tverifyNoMoreInteractions(publisherMock);\n\n\t\tassertThat(captor.getValue().getKey()).isEqualTo(\"1\");\n\t\tassertThat(captor.getValue().getKeyspace()).isEqualTo(Foo.class.getName());\n\t\tassertThat(captor.getValue().getPayload()).isEqualTo(FOO_ONE);\n\t}\n\n\t@Test // DATAKV-91, DATAKV-104, DATAKV-187\n\t@SuppressWarnings({ \"rawtypes\" })\n\tvoid shouldPublishAfterUpdateEventCorrectly() {\n\n\t\tsetEventsToPublish(AfterUpdateEvent.class);\n\n\t\ttemplate.update(\"1\", FOO_ONE);\n\n\t\tArgumentCaptor<AfterUpdateEvent> captor = ArgumentCaptor.forClass(AfterUpdateEvent.class);\n\n\t\tverify(publisherMock, times(1)).publishEvent(captor.capture());\n\t\tverifyNoMoreInteractions(publisherMock);\n\n\t\tassertThat(captor.getValue().getKey()).isEqualTo(\"1\");\n\t\tassertThat(captor.getValue().getKeyspace()).isEqualTo(Foo.class.getName());\n\t\tassertThat(captor.getValue().getPayload()).isEqualTo(FOO_ONE);\n\t}\n\n\t@Test // DATAKV-91, DATAKV-104, DATAKV-187\n\t@SuppressWarnings({ \"rawtypes\" })\n\tvoid shouldPublishBeforeDeleteEventCorrectly() {\n\n\t\tsetEventsToPublish(BeforeDeleteEvent.class);\n\n\t\ttemplate.delete(\"1\", FOO_ONE.getClass());\n\n\t\tArgumentCaptor<BeforeDeleteEvent> captor = ArgumentCaptor.forClass(BeforeDeleteEvent.class);\n\n\t\tverify(publisherMock, times(1)).publishEvent(captor.capture());\n\t\tverifyNoMoreInteractions(publisherMock);\n\n\t\tassertThat(captor.getValue().getKey()).isEqualTo(\"1\");\n\t\tassertThat(captor.getValue().getKeyspace()).isEqualTo(Foo.class.getName());\n\t}\n\n\t@Test // DATAKV-91, DATAKV-104, DATAKV-187\n\t@SuppressWarnings({ \"rawtypes\" })\n\tvoid shouldPublishAfterDeleteEventCorrectly() {\n\n\t\tsetEventsToPublish(AfterDeleteEvent.class);\n\t\twhen(adapterMock.delete(eq(\"1\"), eq(FOO_ONE.getClass().getName()), eq(Foo.class))).thenReturn(FOO_ONE);\n\n\t\ttemplate.delete(\"1\", FOO_ONE.getClass());\n\n\t\tArgumentCaptor<AfterDeleteEvent> captor = ArgumentCaptor.forClass(AfterDeleteEvent.class);\n\n\t\tverify(publisherMock, times(1)).publishEvent(captor.capture());\n\t\tverifyNoMoreInteractions(publisherMock);\n\n\t\tassertThat(captor.getValue().getKey()).isEqualTo(\"1\");\n\t\tassertThat(captor.getValue().getKeyspace()).isEqualTo(Foo.class.getName());\n\t\tassertThat(captor.getValue().getPayload()).isEqualTo(FOO_ONE);\n\t}\n\n\t@Test // DATAKV-91, DATAKV-104, DATAKV-187\n\t@SuppressWarnings({ \"rawtypes\" })\n\tvoid shouldPublishBeforeGetEventCorrectly() {\n\n\t\tsetEventsToPublish(BeforeGetEvent.class);\n\n\t\twhen(adapterMock.get(eq(\"1\"), eq(FOO_ONE.getClass().getName()))).thenReturn(FOO_ONE);\n\n\t\ttemplate.findById(\"1\", FOO_ONE.getClass());\n\n\t\tArgumentCaptor<BeforeGetEvent> captor = ArgumentCaptor.forClass(BeforeGetEvent.class);\n\n\t\tverify(publisherMock, times(1)).publishEvent(captor.capture());\n\t\tverifyNoMoreInteractions(publisherMock);\n\n\t\tassertThat(captor.getValue().getKey()).isEqualTo(\"1\");\n\t\tassertThat(captor.getValue().getKeyspace()).isEqualTo(Foo.class.getName());\n\t}\n\n\t@Test // DATAKV-91, DATAKV-104\n\t@SuppressWarnings({ \"rawtypes\" })\n\tvoid shouldPublishAfterGetEventCorrectly() {\n\n\t\tsetEventsToPublish(AfterGetEvent.class);\n\n\t\twhen(adapterMock.get(eq(\"1\"), eq(FOO_ONE.getClass().getName()), eq(Foo.class))).thenReturn(FOO_ONE);\n\n\t\ttemplate.findById(\"1\", FOO_ONE.getClass());\n\n\t\tArgumentCaptor<AfterGetEvent> captor = ArgumentCaptor.forClass(AfterGetEvent.class);\n\n\t\tverify(publisherMock, times(1)).publishEvent(captor.capture());\n\t\tverifyNoMoreInteractions(publisherMock);\n\n\t\tassertThat(captor.getValue().getKey()).isEqualTo(\"1\");\n\t\tassertThat(captor.getValue().getKeyspace()).isEqualTo(Foo.class.getName());\n\t\tassertThat(captor.getValue().getPayload()).isEqualTo(FOO_ONE);\n\t}\n\n\t@Test // DATAKV-91, DATAKV-104, DATAKV-187\n\t@SuppressWarnings({ \"rawtypes\" })\n\tvoid shouldPublishDropKeyspaceEventCorrectly() {\n\n\t\tsetEventsToPublish(AfterDropKeySpaceEvent.class);\n\n\t\ttemplate.delete(FOO_ONE.getClass());\n\n\t\tArgumentCaptor<AfterDropKeySpaceEvent> captor = ArgumentCaptor.forClass(AfterDropKeySpaceEvent.class);\n\n\t\tverify(publisherMock, times(1)).publishEvent(captor.capture());\n\t\tverifyNoMoreInteractions(publisherMock);\n\n\t\tassertThat(captor.getValue().getKeyspace()).isEqualTo(Foo.class.getName());\n\t}\n\n\t@Test // DATAKV-129\n\tvoid insertShouldRespectTypeAliasUsingAliasFor() {\n\n\t\ttemplate.insert(\"1\", ALIASED_USING_ALIAS_FOR);\n\n\t\tverify(adapterMock, times(1)).put(\"1\", ALIASED_USING_ALIAS_FOR, \"aliased\");\n\t}\n\n\t@SafeVarargs\n\t@SuppressWarnings(\"rawtypes\")\n\tprivate final void setEventsToPublish(Class<? extends KeyValueEvent>... events) {\n\t\ttemplate.setEventTypesToPublish(new HashSet<>(Arrays.asList(events)));\n\t}\n\n\tstatic class Foo {\n\n\t\tString foo;\n\n\t\tpublic Foo(String foo) {\n\t\t\tthis.foo = foo;\n\t\t}\n\n\t\tpublic String getFoo() {\n\t\t\treturn this.foo;\n\t\t}\n\n\t\tpublic void setFoo(String foo) {\n\t\t\tthis.foo = foo;\n\t\t}\n\t}\n\n\tclass Bar {\n\n\t\tString bar;\n\n\t\tpublic Bar(String bar) {\n\t\t\tthis.bar = bar;\n\t\t}\n\n\t\tpublic String getBar() {\n\t\t\treturn this.bar;\n\t\t}\n\n\t\tpublic void setBar(String bar) {\n\t\t\tthis.bar = bar;\n\t\t}\n\t}\n\n\tstatic class ClassWithStringId {\n\n\t\t@Id String id;\n\t\tString value;\n\n\t\tpublic String getId() {\n\t\t\treturn this.id;\n\t\t}\n\n\t\tpublic String getValue() {\n\t\t\treturn this.value;\n\t\t}\n\n\t\tpublic void setId(String id) {\n\t\t\tthis.id = id;\n\t\t}\n\n\t\tpublic void setValue(String value) {\n\t\t\tthis.value = value;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/core/PredicateQueryEngineUnitTests.java",
    "content": "/*\n * Copyright 2024-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport static org.assertj.core.api.Assertions.*;\nimport static org.mockito.Mockito.*;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.function.Predicate;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.core.TypeInformation;\nimport org.springframework.data.keyvalue.repository.query.PredicateQueryCreator;\nimport org.springframework.data.projection.SpelAwareProxyProjectionFactory;\nimport org.springframework.data.repository.core.RepositoryMetadata;\nimport org.springframework.data.repository.query.ParametersParameterAccessor;\nimport org.springframework.data.repository.query.QueryMethod;\nimport org.springframework.data.repository.query.parser.PartTree;\n\n/**\n * Unit tests for {@link SpelQueryEngine}.\n *\n * @author Christoph Strobl\n */\n@ExtendWith(MockitoExtension.class)\npublic class PredicateQueryEngineUnitTests {\n\n\tprivate static final Person BOB_WITH_FIRSTNAME = new Person(\"bob\", 30);\n\tprivate static final Person MIKE_WITHOUT_FIRSTNAME = new Person(null, 25);\n\n\t@Mock KeyValueAdapter adapter;\n\n\tprivate PredicateQueryEngine engine;\n\n\tprivate Iterable<Person> people = Arrays.asList(BOB_WITH_FIRSTNAME, MIKE_WITHOUT_FIRSTNAME);\n\n\t@BeforeEach\n\tvoid setUp() {\n\n\t\tengine = new PredicateQueryEngine();\n\t\tengine.registerAdapter(adapter);\n\t}\n\n\t@Test // DATAKV-114\n\t@SuppressWarnings(\"unchecked\")\n\tvoid queriesEntitiesWithNullProperty() throws Exception {\n\n\t\tdoReturn(people).when(adapter).getAllOf(anyString());\n\n\t\tCollection result = engine.execute(createQueryForMethodWithArgs(\"findByFirstname\", \"bob\"), null, -1, -1,\n\t\t\t\tanyString());\n\t\tassertThat(result).containsExactly(BOB_WITH_FIRSTNAME);\n\t}\n\n\t@Test // DATAKV-114\n\tvoid countsEntitiesWithNullProperty() throws Exception {\n\n\t\tdoReturn(people).when(adapter).getAllOf(anyString());\n\n\t\tassertThat(engine.count(createQueryForMethodWithArgs(\"findByFirstname\", \"bob\"), anyString())).isEqualTo(1L);\n\t}\n\n\tprivate static Predicate<?> createQueryForMethodWithArgs(String methodName, Object... args) throws Exception {\n\n\t\tList<Class<?>> types = new ArrayList<>(args.length);\n\n\t\tfor (Object arg : args) {\n\t\t\ttypes.add(arg.getClass());\n\t\t}\n\n\t\tMethod method = PersonRepository.class.getMethod(methodName, types.toArray(new Class<?>[types.size()]));\n\t\tRepositoryMetadata metadata = mock(RepositoryMetadata.class);\n\t\tdoReturn(method.getReturnType()).when(metadata).getReturnedDomainClass(method);\n\t\tdoReturn(TypeInformation.fromReturnTypeOf(method)).when(metadata).getReturnType(method);\n\t\tdoReturn(TypeInformation.of(Person.class)).when(metadata).getDomainTypeInformation();\n\n\t\tPartTree partTree = new PartTree(method.getName(), method.getReturnType());\n\t\tPredicateQueryCreator creator = new PredicateQueryCreator(partTree, new ParametersParameterAccessor(\n\t\t\t\tnew QueryMethod(method, metadata, new SpelAwareProxyProjectionFactory()).getParameters(), args));\n\n\t\treturn creator.createQuery().getCriteria();\n\t}\n\n\tinterface PersonRepository {\n\t\tPerson findByFirstname(String firstname);\n\t}\n\n\tpublic static class Person {\n\n\t\t@Id String id;\n\t\tString firstname;\n\t\tint age;\n\n\t\tPerson(String firstname, int age) {\n\n\t\t\tthis.firstname = firstname;\n\t\t\tthis.age = age;\n\t\t}\n\n\t\tpublic String getFirstname() {\n\t\t\treturn firstname;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/core/PropertyPathComparatorUnitTests.java",
    "content": "/*\n * Copyright 2024-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport static org.assertj.core.api.Assertions.*;\n\nimport java.util.Comparator;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link PropertyPathComparator}.\n *\n * @author Christoph Strobl\n */\nclass PropertyPathComparatorUnitTests {\n\n\tprivate static final SomeType ONE = new SomeType(\"one\", 1, 1);\n\tprivate static final SomeType TWO = new SomeType(\"two\", 2, 2);\n\tprivate static final WrapperType WRAPPER_ONE = new WrapperType(\"w-one\", ONE);\n\tprivate static final WrapperType WRAPPER_TWO = new WrapperType(\"w-two\", TWO);\n\n\t@Test // DATACMNS-525\n\tvoid shouldCompareStringAscCorrectly() {\n\n\t\tComparator<SomeType> comparator = new PropertyPathComparator<>(\"stringProperty\");\n\t\tassertThat(comparator.compare(ONE, TWO)).isEqualTo(ONE.getStringProperty().compareTo(TWO.getStringProperty()));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid shouldCompareStringDescCorrectly() {\n\n\t\tComparator<SomeType> comparator = new PropertyPathComparator<SomeType>(\"stringProperty\").desc();\n\t\tassertThat(comparator.compare(ONE, TWO)).isEqualTo(TWO.getStringProperty().compareTo(ONE.getStringProperty()));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid shouldCompareIntegerAscCorrectly() {\n\n\t\tComparator<SomeType> comparator = new PropertyPathComparator<>(\"integerProperty\");\n\t\tassertThat(comparator.compare(ONE, TWO)).isEqualTo(ONE.getIntegerProperty().compareTo(TWO.getIntegerProperty()));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid shouldCompareIntegerDescCorrectly() {\n\n\t\tComparator<SomeType> comparator = new PropertyPathComparator<SomeType>(\"integerProperty\").desc();\n\t\tassertThat(comparator.compare(ONE, TWO)).isEqualTo(TWO.getIntegerProperty().compareTo(ONE.getIntegerProperty()));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid shouldComparePrimitiveIntegerAscCorrectly() {\n\n\t\tComparator<SomeType> comparator = new PropertyPathComparator<>(\"primitiveProperty\");\n\t\tassertThat(comparator.compare(ONE, TWO))\n\t\t\t\t.isEqualTo(Integer.compare(ONE.getPrimitiveProperty(), TWO.getPrimitiveProperty()));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid shouldNotFailOnNullValues() {\n\n\t\tComparator<SomeType> comparator = new PropertyPathComparator<>(\"stringProperty\");\n\t\tassertThat(comparator.compare(ONE, new SomeType(null, null, 2))).isEqualTo(1);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid shouldComparePrimitiveIntegerDescCorrectly() {\n\n\t\tComparator<SomeType> comparator = new PropertyPathComparator<SomeType>(\"primitiveProperty\").desc();\n\t\tassertThat(comparator.compare(ONE, TWO))\n\t\t\t\t.isEqualTo(Integer.compare(TWO.getPrimitiveProperty(), ONE.getPrimitiveProperty()));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid shouldSortNullsFirstCorrectly() {\n\t\tComparator<SomeType> comparator = new PropertyPathComparator<SomeType>(\"stringProperty\").nullsFirst();\n\t\tassertThat(comparator.compare(ONE, new SomeType(null, null, 2))).isEqualTo(1);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid shouldSortNullsLastCorrectly() {\n\n\t\tComparator<SomeType> comparator = new PropertyPathComparator<SomeType>(\"stringProperty\").nullsLast();\n\t\tassertThat(comparator.compare(ONE, new SomeType(null, null, 2))).isEqualTo(-1);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid shouldCompareNestedTypesCorrectly() {\n\n\t\tComparator<WrapperType> comparator = new PropertyPathComparator<>(\"nestedType.stringProperty\");\n\t\tassertThat(comparator.compare(WRAPPER_ONE, WRAPPER_TWO)).isEqualTo(\n\t\t\t\tWRAPPER_ONE.getNestedType().getStringProperty().compareTo(WRAPPER_TWO.getNestedType().getStringProperty()));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid shouldCompareNestedTypesCorrectlyWhenOneOfThemHasNullValue() {\n\n\t\tPropertyPathComparator<WrapperType> comparator = new PropertyPathComparator<>(\"nestedType.stringProperty\");\n\t\tassertThat(comparator.compare(WRAPPER_ONE, new WrapperType(\"two\", null))).isGreaterThanOrEqualTo(1);\n\t}\n\n\tpublic static class WrapperType {\n\n\t\tprivate String stringPropertyWrapper;\n\t\tprivate SomeType nestedType;\n\n\t\tWrapperType(String stringPropertyWrapper, SomeType nestedType) {\n\t\t\tthis.stringPropertyWrapper = stringPropertyWrapper;\n\t\t\tthis.nestedType = nestedType;\n\t\t}\n\n\t\tpublic String getStringPropertyWrapper() {\n\t\t\treturn stringPropertyWrapper;\n\t\t}\n\n\t\tpublic void setStringPropertyWrapper(String stringPropertyWrapper) {\n\t\t\tthis.stringPropertyWrapper = stringPropertyWrapper;\n\t\t}\n\n\t\tpublic SomeType getNestedType() {\n\t\t\treturn nestedType;\n\t\t}\n\n\t\tpublic void setNestedType(SomeType nestedType) {\n\t\t\tthis.nestedType = nestedType;\n\t\t}\n\n\t}\n\n\t@SuppressWarnings(\"WeakerAccess\")\n\tpublic static class SomeType {\n\n\t\tpublic SomeType() {\n\n\t\t}\n\n\t\tSomeType(String stringProperty, Integer integerProperty, int primitiveProperty) {\n\t\t\tthis.stringProperty = stringProperty;\n\t\t\tthis.integerProperty = integerProperty;\n\t\t\tthis.primitiveProperty = primitiveProperty;\n\t\t}\n\n\t\tString stringProperty;\n\t\tInteger integerProperty;\n\t\tint primitiveProperty;\n\n\t\tpublic String getStringProperty() {\n\t\t\treturn stringProperty;\n\t\t}\n\n\t\tpublic void setStringProperty(String stringProperty) {\n\t\t\tthis.stringProperty = stringProperty;\n\t\t}\n\n\t\tpublic Integer getIntegerProperty() {\n\t\t\treturn integerProperty;\n\t\t}\n\n\t\tpublic void setIntegerProperty(Integer integerProperty) {\n\t\t\tthis.integerProperty = integerProperty;\n\t\t}\n\n\t\tpublic int getPrimitiveProperty() {\n\t\t\treturn primitiveProperty;\n\t\t}\n\n\t\tpublic void setPrimitiveProperty(int primitiveProperty) {\n\t\t\tthis.primitiveProperty = primitiveProperty;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/core/SpelPropertyComparatorUnitTests.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport static org.assertj.core.api.Assertions.*;\n\nimport java.util.Comparator;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\n\n/**\n * Unit tests for {@link SpelPropertyComparator}.\n *\n * @author Christoph Strobl\n * @author Oliver Gierke\n * @author Artur Ciocanu\n */\nclass SpelPropertyComparatorUnitTests {\n\n\tprivate static final SpelExpressionParser PARSER = new SpelExpressionParser();\n\n\tprivate static final SomeType ONE = new SomeType(\"one\", 1, 1);\n\tprivate static final SomeType TWO = new SomeType(\"two\", 2, 2);\n\tprivate static final WrapperType WRAPPER_ONE = new WrapperType(\"w-one\", ONE);\n\tprivate static final WrapperType WRAPPER_TWO = new WrapperType(\"w-two\", TWO);\n\n\t@Test // DATACMNS-525\n\tvoid shouldCompareStringAscCorrectly() {\n\n\t\tComparator<SomeType> comparator = new SpelPropertyComparator<>(\"stringProperty\", PARSER);\n\t\tassertThat(comparator.compare(ONE, TWO)).isEqualTo(ONE.getStringProperty().compareTo(TWO.getStringProperty()));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid shouldCompareStringDescCorrectly() {\n\n\t\tComparator<SomeType> comparator = new SpelPropertyComparator<SomeType>(\"stringProperty\", PARSER).desc();\n\t\tassertThat(comparator.compare(ONE, TWO)).isEqualTo(TWO.getStringProperty().compareTo(ONE.getStringProperty()));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid shouldCompareIntegerAscCorrectly() {\n\n\t\tComparator<SomeType> comparator = new SpelPropertyComparator<>(\"integerProperty\", PARSER);\n\t\tassertThat(comparator.compare(ONE, TWO)).isEqualTo(ONE.getIntegerProperty().compareTo(TWO.getIntegerProperty()));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid shouldCompareIntegerDescCorrectly() {\n\n\t\tComparator<SomeType> comparator = new SpelPropertyComparator<SomeType>(\"integerProperty\", PARSER).desc();\n\t\tassertThat(comparator.compare(ONE, TWO)).isEqualTo(TWO.getIntegerProperty().compareTo(ONE.getIntegerProperty()));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid shouldComparePrimitiveIntegerAscCorrectly() {\n\n\t\tComparator<SomeType> comparator = new SpelPropertyComparator<>(\"primitiveProperty\", PARSER);\n\t\tassertThat(comparator.compare(ONE, TWO))\n\t\t\t\t.isEqualTo(Integer.compare(ONE.getPrimitiveProperty(), TWO.getPrimitiveProperty()));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid shouldNotFailOnNullValues() {\n\n\t\tComparator<SomeType> comparator = new SpelPropertyComparator<>(\"stringProperty\", PARSER);\n\t\tassertThat(comparator.compare(ONE, new SomeType(null, null, 2))).isEqualTo(1);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid shouldComparePrimitiveIntegerDescCorrectly() {\n\n\t\tComparator<SomeType> comparator = new SpelPropertyComparator<SomeType>(\"primitiveProperty\", PARSER).desc();\n\t\tassertThat(comparator.compare(ONE, TWO))\n\t\t\t\t.isEqualTo(Integer.compare(TWO.getPrimitiveProperty(), ONE.getPrimitiveProperty()));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid shouldSortNullsFirstCorrectly() {\n\t\tComparator<SomeType> comparator = new SpelPropertyComparator<SomeType>(\"stringProperty\", PARSER).nullsFirst();\n\t\tassertThat(comparator.compare(ONE, new SomeType(null, null, 2))).isEqualTo(1);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid shouldSortNullsLastCorrectly() {\n\n\t\tComparator<SomeType> comparator = new SpelPropertyComparator<SomeType>(\"stringProperty\", PARSER).nullsLast();\n\t\tassertThat(comparator.compare(ONE, new SomeType(null, null, 2))).isEqualTo(-1);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid shouldCompareNestedTypesCorrectly() {\n\n\t\tComparator<WrapperType> comparator = new SpelPropertyComparator<>(\"nestedType.stringProperty\", PARSER);\n\t\tassertThat(comparator.compare(WRAPPER_ONE, WRAPPER_TWO)).isEqualTo(\n\t\t\t\tWRAPPER_ONE.getNestedType().getStringProperty().compareTo(WRAPPER_TWO.getNestedType().getStringProperty()));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid shouldCompareNestedTypesCorrectlyWhenOneOfThemHasNullValue() {\n\n\t\tSpelPropertyComparator<WrapperType> comparator = new SpelPropertyComparator<>(\"nestedType.stringProperty\", PARSER);\n\t\tassertThat(comparator.compare(WRAPPER_ONE, new WrapperType(\"two\", null))).isGreaterThanOrEqualTo(1);\n\t}\n\n\tpublic static class WrapperType {\n\n\t\tprivate String stringPropertyWrapper;\n\t\tprivate SomeType nestedType;\n\n\t\tWrapperType(String stringPropertyWrapper, SomeType nestedType) {\n\t\t\tthis.stringPropertyWrapper = stringPropertyWrapper;\n\t\t\tthis.nestedType = nestedType;\n\t\t}\n\n\t\tpublic String getStringPropertyWrapper() {\n\t\t\treturn stringPropertyWrapper;\n\t\t}\n\n\t\tpublic void setStringPropertyWrapper(String stringPropertyWrapper) {\n\t\t\tthis.stringPropertyWrapper = stringPropertyWrapper;\n\t\t}\n\n\t\tpublic SomeType getNestedType() {\n\t\t\treturn nestedType;\n\t\t}\n\n\t\tpublic void setNestedType(SomeType nestedType) {\n\t\t\tthis.nestedType = nestedType;\n\t\t}\n\n\t}\n\n\t@SuppressWarnings(\"WeakerAccess\")\n\tpublic static class SomeType {\n\n\t\tpublic SomeType() {\n\n\t\t}\n\n\t\tSomeType(String stringProperty, Integer integerProperty, int primitiveProperty) {\n\t\t\tthis.stringProperty = stringProperty;\n\t\t\tthis.integerProperty = integerProperty;\n\t\t\tthis.primitiveProperty = primitiveProperty;\n\t\t}\n\n\t\tString stringProperty;\n\t\tInteger integerProperty;\n\t\tint primitiveProperty;\n\n\t\tpublic String getStringProperty() {\n\t\t\treturn stringProperty;\n\t\t}\n\n\t\tpublic void setStringProperty(String stringProperty) {\n\t\t\tthis.stringProperty = stringProperty;\n\t\t}\n\n\t\tpublic Integer getIntegerProperty() {\n\t\t\treturn integerProperty;\n\t\t}\n\n\t\tpublic void setIntegerProperty(Integer integerProperty) {\n\t\t\tthis.integerProperty = integerProperty;\n\t\t}\n\n\t\tpublic int getPrimitiveProperty() {\n\t\t\treturn primitiveProperty;\n\t\t}\n\n\t\tpublic void setPrimitiveProperty(int primitiveProperty) {\n\t\t\tthis.primitiveProperty = primitiveProperty;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/core/SpelQueryEngineUnitTests.java",
    "content": "/*\n * Copyright 2015-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core;\n\nimport static org.assertj.core.api.Assertions.*;\nimport static org.mockito.Mockito.*;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.core.TypeInformation;\nimport org.springframework.data.keyvalue.repository.query.SpelQueryCreator;\nimport org.springframework.data.projection.SpelAwareProxyProjectionFactory;\nimport org.springframework.data.repository.core.RepositoryMetadata;\nimport org.springframework.data.repository.query.ParametersParameterAccessor;\nimport org.springframework.data.repository.query.QueryMethod;\nimport org.springframework.data.repository.query.parser.PartTree;\nimport org.springframework.expression.spel.support.SimpleEvaluationContext;\n\n/**\n * Unit tests for {@link SpelQueryEngine}.\n *\n * @author Martin Macko\n * @author Oliver Gierke\n * @author Mark Paluch\n */\n@ExtendWith(MockitoExtension.class)\npublic class SpelQueryEngineUnitTests {\n\n\tprivate static final Person BOB_WITH_FIRSTNAME = new Person(\"bob\", 30);\n\tprivate static final Person MIKE_WITHOUT_FIRSTNAME = new Person(null, 25);\n\n\t@Mock KeyValueAdapter adapter;\n\n\tprivate SpelQueryEngine engine;\n\n\tprivate Iterable<Person> people = Arrays.asList(BOB_WITH_FIRSTNAME, MIKE_WITHOUT_FIRSTNAME);\n\n\t@BeforeEach\n\tvoid setUp() {\n\n\t\tengine = new SpelQueryEngine();\n\t\tengine.registerAdapter(adapter);\n\t}\n\n\t@Test // DATAKV-114\n\t@SuppressWarnings(\"unchecked\")\n\tvoid queriesEntitiesWithNullProperty() throws Exception {\n\n\t\tdoReturn(people).when(adapter).getAllOf(anyString());\n\n\t\tCollection result = engine.execute(createQueryForMethodWithArgs(\"findByFirstname\", \"bob\"), null, -1, -1,\n\t\t\t\tanyString());\n\t\tassertThat(result).containsExactly(BOB_WITH_FIRSTNAME);\n\t}\n\n\t@Test // DATAKV-114\n\tvoid countsEntitiesWithNullProperty() throws Exception {\n\n\t\tdoReturn(people).when(adapter).getAllOf(anyString());\n\n\t\tassertThat(engine.count(createQueryForMethodWithArgs(\"findByFirstname\", \"bob\"), anyString())).isEqualTo(1L);\n\t}\n\n\tprivate static SpelCriteria createQueryForMethodWithArgs(String methodName, Object... args) throws Exception {\n\n\t\tList<Class<?>> types = new ArrayList<>(args.length);\n\n\t\tfor (Object arg : args) {\n\t\t\ttypes.add(arg.getClass());\n\t\t}\n\n\t\tMethod method = PersonRepository.class.getMethod(methodName, types.toArray(new Class<?>[types.size()]));\n\t\tRepositoryMetadata metadata = mock(RepositoryMetadata.class);\n\t\tdoReturn(method.getReturnType()).when(metadata).getReturnedDomainClass(method);\n\t\tdoReturn(TypeInformation.fromReturnTypeOf(method)).when(metadata).getReturnType(method);\n\t\tdoReturn(TypeInformation.of(Person.class)).when(metadata).getDomainTypeInformation();\n\n\t\tPartTree partTree = new PartTree(method.getName(), method.getReturnType());\n\t\tSpelQueryCreator creator = new SpelQueryCreator(partTree, new ParametersParameterAccessor(\n\t\t\t\tnew QueryMethod(method, metadata, new SpelAwareProxyProjectionFactory()).getParameters(), args));\n\n\t\treturn new SpelCriteria(creator.createQuery().getCriteria(),\n\t\t\t\tSimpleEvaluationContext.forReadOnlyDataBinding().withInstanceMethods().withRootObject(args).build());\n\t}\n\n\tinterface PersonRepository {\n\t\tPerson findByFirstname(String firstname);\n\t}\n\n\tpublic static class Person {\n\n\t\t@Id String id;\n\t\tString firstname;\n\t\tint age;\n\n\t\tPerson(String firstname, int age) {\n\n\t\t\tthis.firstname = firstname;\n\t\t\tthis.age = age;\n\t\t}\n\n\t\tpublic String getFirstname() {\n\t\t\treturn firstname;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/core/mapping/AnnotationBasedKeySpaceResolverUnitTests.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core.mapping;\n\nimport static org.assertj.core.api.Assertions.*;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.annotation.AliasFor;\nimport org.springframework.data.annotation.Persistent;\nimport org.springframework.data.keyvalue.TypeWithDirectKeySpaceAnnotation;\nimport org.springframework.data.keyvalue.TypeWithInhteritedPersistentAnnotationNotHavingKeySpace;\nimport org.springframework.data.keyvalue.TypeWithPersistentAnnotationNotHavingKeySpace;\nimport org.springframework.data.keyvalue.annotation.KeySpace;\n\n/**\n * Unit tests for {@link AnnotationBasedKeySpaceResolver}.\n *\n * @author Christoph Strobl\n * @author Oliver Gierke\n */\nclass AnnotationBasedKeySpaceResolverUnitTests {\n\n\tprivate AnnotationBasedKeySpaceResolver resolver;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tresolver = AnnotationBasedKeySpaceResolver.INSTANCE;\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid shouldResolveKeySpaceDefaultValueCorrectly() {\n\t\tassertThat(resolver.resolveKeySpace(EntityWithDefaultKeySpace.class)).isEqualTo(\"daenerys\");\n\t}\n\n\t@Test // DATAKV-105\n\tvoid shouldReturnNullWhenNoKeySpaceFoundOnComposedPersistentAnnotation() {\n\t\tassertThat(resolver.resolveKeySpace(TypeWithInhteritedPersistentAnnotationNotHavingKeySpace.class)).isNull();\n\t}\n\n\t@Test // DATAKV-105\n\tvoid shouldReturnNullWhenPersistentIsFoundOnNonComposedAnnotation() {\n\t\tassertThat(resolver.resolveKeySpace(TypeWithPersistentAnnotationNotHavingKeySpace.class)).isNull();\n\t}\n\n\t@Test // DATAKV-105\n\tvoid shouldReturnNullWhenPersistentIsNotFound() {\n\t\tassertThat(resolver.resolveKeySpace(TypeWithoutKeySpace.class)).isNull();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid shouldResolveInheritedKeySpaceCorrectly() {\n\t\tassertThat(resolver.resolveKeySpace(EntityWithInheritedKeySpace.class)).isEqualTo(\"viserys\");\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid shouldResolveDirectKeySpaceAnnotationCorrectly() {\n\t\tassertThat(resolver.resolveKeySpace(TypeWithDirectKeySpaceAnnotation.class)).isEqualTo(\"rhaegar\");\n\t}\n\n\t@Test // DATAKV-129\n\tvoid shouldResolveKeySpaceUsingAliasForCorrectly() {\n\t\tassertThat(resolver.resolveKeySpace(EntityWithSetKeySpaceUsingAliasFor.class)).isEqualTo(\"viserys\");\n\t}\n\n\t@Test // DATAKV-129\n\tvoid shouldResolveKeySpaceUsingAliasForCorrectlyOnSubClass() {\n\t\tassertThat(resolver.resolveKeySpace(EntityWithInheritedKeySpaceUsingAliasFor.class)).isEqualTo(\"viserys\");\n\t}\n\n\t@PersistentAnnotationWithExplicitKeySpaceUsingAliasFor\n\tprivate static class EntityWithDefaultKeySpace {\n\n\t}\n\n\t@PersistentAnnotationWithExplicitKeySpaceUsingAliasFor(firstname = \"viserys\")\n\tstatic class EntityWithSetKeySpaceUsingAliasFor {\n\n\t}\n\n\tprivate static class EntityWithInheritedKeySpace extends EntityWithSetKeySpaceUsingAliasFor {\n\n\t}\n\n\tprivate static class EntityWithInheritedKeySpaceUsingAliasFor extends EntityWithSetKeySpaceUsingAliasFor {\n\n\t}\n\n\t@Persistent\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target({ ElementType.TYPE })\n\t@KeySpace\n\tstatic @interface PersistentAnnotationWithExplicitKeySpaceUsingAliasFor {\n\n\t\t@AliasFor(annotation = KeySpace.class, attribute = \"value\")\n\t\tString firstname() default \"daenerys\";\n\n\t\tString lastname() default \"targaryen\";\n\n\t}\n\n\tstatic class TypeWithoutKeySpace {\n\n\t\tString foo;\n\n\t}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/core/mapping/BasicKeyValuePersistentEntityUnitTests.java",
    "content": "/*\n * Copyright 2019-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core.mapping;\n\nimport static org.assertj.core.api.Assertions.*;\n\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\nimport org.springframework.data.keyvalue.annotation.KeySpace;\nimport org.springframework.data.keyvalue.core.mapping.context.KeyValueMappingContext;\nimport org.springframework.data.spel.ExtensionAwareEvaluationContextProvider;\nimport org.springframework.data.spel.spi.EvaluationContextExtension;\nimport org.springframework.mock.env.MockEnvironment;\n\n/**\n * Unit tests for {@link BasicKeyValuePersistentEntity}.\n *\n * @author Mark Paluch\n */\nclass BasicKeyValuePersistentEntityUnitTests {\n\n\tKeyValueMappingContext<? extends KeyValuePersistentEntity<?, ?>, ? extends KeyValuePersistentProperty<?>> mappingContext = new KeyValueMappingContext<>();\n\n\t@Test // DATAKV-268\n\tvoid shouldDeriveKeyspaceFromClassName() {\n\n\t\tassertThat(mappingContext.getPersistentEntity(KeyspaceEntity.class).getRequiredKeySpace())\n\t\t\t\t.isEqualTo(KeyspaceEntity.class.getName());\n\t}\n\n\t@Test // DATAKV-268, GH-613\n\tvoid shouldEvaluateKeyspaceExpression() {\n\n\t\tMockEnvironment environment = new MockEnvironment().withProperty(\"my.property\", \"foo\");\n\t\tmappingContext.setEnvironment(environment);\n\n\t\tKeyValuePersistentEntity<?, ?> persistentEntity = mappingContext.getPersistentEntity(ExpressionEntity.class);\n\t\tpersistentEntity.setEvaluationContextProvider(\n\t\t\t\tnew ExtensionAwareEvaluationContextProvider(Collections.singletonList(new SampleExtension())));\n\n\t\tassertThat(persistentEntity.getRequiredKeySpace()).isEqualTo(\"some_foo\");\n\t}\n\n\t@Test // DATAKV-268\n\tvoid shouldEvaluateEntityWithoutKeyspace() {\n\n\t\tKeyValuePersistentEntity<?, ?> persistentEntity = mappingContext.getPersistentEntity(NoKeyspaceEntity.class);\n\t\tpersistentEntity.setEvaluationContextProvider(\n\t\t\t\tnew ExtensionAwareEvaluationContextProvider(Collections.singletonList(new SampleExtension())));\n\n\t\tassertThat(persistentEntity.getRequiredKeySpace()).isEqualTo(NoKeyspaceEntity.class.getName());\n\t}\n\n\t@Test // GH-461\n\tvoid shouldApplyKeySpaceResolver() {\n\n\t\tmappingContext.setKeySpaceResolver(new PrefixKeyspaceResolver(\"foo\", ClassNameKeySpaceResolver.INSTANCE));\n\t\tKeyValuePersistentEntity<?, ?> persistentEntity = mappingContext.getPersistentEntity(NoKeyspaceEntity.class);\n\n\t\tassertThat(persistentEntity.getKeySpace()).isEqualTo(\"foo\" + NoKeyspaceEntity.class.getName());\n\t}\n\n\t@Test // GH-461\n\tvoid shouldFallBackToDefaultsIfKeySpaceResolverReturnsNull() {\n\n\t\tmappingContext.setKeySpaceResolver(it -> null);\n\t\tKeyValuePersistentEntity<?, ?> persistentEntity = mappingContext.getPersistentEntity(NoKeyspaceEntity.class);\n\n\t\tassertThat(persistentEntity.getKeySpace()).isEqualTo(NoKeyspaceEntity.class.getName());\n\t}\n\n\t@KeySpace(\"#{myProperty}_${my.property}\")\n\tprivate static class ExpressionEntity {}\n\n\t@KeySpace\n\tprivate static class KeyspaceEntity {}\n\n\tprivate static class NoKeyspaceEntity {}\n\n\tstatic class SampleExtension implements EvaluationContextExtension {\n\n\t\t@Override\n\t\tpublic String getExtensionId() {\n\t\t\treturn \"sampleExtension\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, Object> getProperties() {\n\n\t\t\tMap<String, Object> properties = new LinkedHashMap<>();\n\t\t\tproperties.put(\"myProperty\", \"some\");\n\t\t\treturn properties;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/core/mapping/PrefixKeyspaceResolverUnitTests.java",
    "content": "/*\n * Copyright 2022-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core.mapping;\n\nimport static org.assertj.core.api.Assertions.*;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link PrefixKeyspaceResolver}.\n *\n * @author Mark Paluch\n */\nclass PrefixKeyspaceResolverUnitTests {\n\n\t@Test // gh-461\n\tvoid shouldApplyPrefix() {\n\n\t\tvar resolver = new PrefixKeyspaceResolver(\"foo\", Class::getSimpleName);\n\n\t\tassertThat(resolver.resolveKeySpace(Object.class)).isEqualTo(\"fooObject\");\n\t}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/core/mapping/context/KeyValueMappingContextUnitTests.java",
    "content": "/*\n * Copyright 2021-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.core.mapping.context;\n\nimport static org.assertj.core.api.Assertions.*;\n\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.util.UUID;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.data.keyvalue.core.mapping.KeyValuePersistentEntity;\nimport org.springframework.data.keyvalue.core.mapping.KeyValuePersistentProperty;\n\n/**\n * Unit test for {@link KeyValueMappingContext}.\n *\n * @author Mark Paluch\n */\nclass KeyValueMappingContextUnitTests<P extends KeyValuePersistentProperty<P>> {\n\n\t@Test\n\tvoid shouldNotCreateEntitiesForJavaStandardTypes() {\n\n\t\tKeyValueMappingContext<KeyValuePersistentEntity<?, P>, P> mappingContext = new KeyValueMappingContext<>();\n\n\t\tassertThat(mappingContext.getPersistentEntity(BigInteger.class)).isNull();\n\t\tassertThat(mappingContext.getPersistentEntity(BigDecimal.class)).isNull();\n\t\tassertThat(mappingContext.getPersistentEntity(UUID.class)).isNull();\n\t}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/repository/MapRepositoriesRegistrarUnitTests.java",
    "content": "/*\n * Copyright 2024-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.repository;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.Arrays;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.beans.factory.support.DefaultListableBeanFactory;\nimport org.springframework.context.annotation.AnnotationBeanNameGenerator;\nimport org.springframework.core.env.StandardEnvironment;\nimport org.springframework.core.io.DefaultResourceLoader;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.data.map.repository.config.EnableMapRepositories;\nimport org.springframework.data.map.repository.config.MapRepositoriesRegistrar;\nimport org.springframework.data.repository.CrudRepository;\n\n/**\n * @author Christoph Strobl\n */\nclass MapRepositoriesRegistrarUnitTests {\n\n\tprivate BeanDefinitionRegistry registry;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tregistry = new DefaultListableBeanFactory();\n\t}\n\n\t@ParameterizedTest // GH-499, GH-3440\n\t@MethodSource(value = { \"args\" })\n\tvoid configuresRepositoriesCorrectly(AnnotationMetadata metadata, String[] beanNames) {\n\n\t\tMapRepositoriesRegistrar registrar = new MapRepositoriesRegistrar();\n\t\tregistrar.setResourceLoader(new DefaultResourceLoader());\n\t\tregistrar.setEnvironment(new StandardEnvironment());\n\t\tregistrar.registerBeanDefinitions(metadata, registry);\n\n\t\tIterable<String> names = Arrays.asList(registry.getBeanDefinitionNames());\n\t\tassertThat(names).contains(beanNames);\n\t}\n\n\tstatic Stream<Arguments> args() {\n\t\treturn Stream.of(\n\t\t\t\tArguments.of(AnnotationMetadata.introspect(Config.class),\n\t\t\t\t\t\tnew String[] { \"mapRepositoriesRegistrarUnitTests.PersonRepository\" }),\n\t\t\t\tArguments.of(AnnotationMetadata.introspect(ConfigWithBeanNameGenerator.class),\n\t\t\t\t\t\tnew String[] { \"mapRepositoriesRegistrarUnitTests.PersonREPO\" }));\n\t}\n\n\t@EnableMapRepositories(basePackageClasses = PersonRepository.class, considerNestedRepositories = true)\n\tprivate class Config {\n\n\t}\n\n\t@EnableMapRepositories(basePackageClasses = PersonRepository.class, nameGenerator = MyBeanNameGenerator.class,\n\t\t\tconsiderNestedRepositories = true)\n\tprivate class ConfigWithBeanNameGenerator {\n\n\t}\n\n\tstatic class MyBeanNameGenerator extends AnnotationBeanNameGenerator {\n\n\t\t@Override\n\t\tpublic String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {\n\t\t\treturn super.generateBeanName(definition, registry).replaceAll(\"Repository\", \"REPO\");\n\t\t}\n\t}\n\n\tinterface PersonRepository extends CrudRepository<Person, String> {\n\n\t}\n\n\tstatic class Person {}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/repository/SimpleKeyValueRepositoryUnitTests.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.repository;\n\nimport static org.assertj.core.api.Assertions.*;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.annotation.Persistent;\nimport org.springframework.data.domain.PageRequest;\nimport org.springframework.data.domain.Pageable;\nimport org.springframework.data.domain.Sort;\nimport org.springframework.data.keyvalue.core.KeyValueOperations;\nimport org.springframework.data.keyvalue.core.mapping.context.KeyValueMappingContext;\nimport org.springframework.data.keyvalue.repository.support.SimpleKeyValueRepository;\nimport org.springframework.data.mapping.PersistentEntity;\nimport org.springframework.data.repository.core.EntityInformation;\nimport org.springframework.data.repository.core.support.PersistentEntityInformation;\n\n/**\n * @author Christoph Strobl\n * @author Eugene Nikiforov\n * @author Jens Schauder\n * @author Mark Paluch\n */\n@ExtendWith(MockitoExtension.class)\nclass SimpleKeyValueRepositoryUnitTests {\n\n\tprivate SimpleKeyValueRepository<Foo, String> repo;\n\tprivate @Mock KeyValueOperations opsMock;\n\tprivate KeyValueMappingContext<?, ?> context;\n\n\t@BeforeEach\n\tvoid setUp() {\n\n\t\tthis.context = new KeyValueMappingContext<>();\n\n\t\tEntityInformation<Foo, String> ei = getEntityInformationFor(Foo.class);\n\t\trepo = new SimpleKeyValueRepository<>(ei, opsMock);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid saveNewWithNumericId() {\n\n\t\tEntityInformation<WithNumericId, ?> ei = getEntityInformationFor(WithNumericId.class);\n\t\tSimpleKeyValueRepository<WithNumericId, ?> temp = new SimpleKeyValueRepository<>(ei, opsMock);\n\n\t\tWithNumericId withNumericId = new WithNumericId();\n\t\ttemp.save(withNumericId);\n\n\t\tverify(opsMock, times(1)).insert(eq(withNumericId));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid testDoubleSave() {\n\n\t\tFoo foo = new Foo(\"one\");\n\n\t\trepo.save(foo);\n\n\t\tfoo.id = \"1\";\n\t\trepo.save(foo);\n\t\tverify(opsMock, times(1)).insert(eq(foo));\n\t\tverify(opsMock, times(1)).update(eq(foo.getId()), eq(foo));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid multipleSave() {\n\n\t\tFoo one = new Foo(\"one\");\n\t\tFoo two = new Foo(\"two\");\n\n\t\trepo.saveAll(Arrays.asList(one, two));\n\t\tverify(opsMock, times(1)).insert(eq(one));\n\t\tverify(opsMock, times(1)).insert(eq(two));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid deleteEntity() {\n\n\t\tFoo one = new Foo(\"one\");\n\t\tone.id = \"1\";\n\t\trepo.save(one);\n\t\trepo.delete(one);\n\n\t\tverify(opsMock, times(1)).delete(eq(one));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid deleteById() {\n\n\t\trepo.deleteById(\"one\");\n\n\t\tverify(opsMock, times(1)).delete(eq(\"one\"), eq(Foo.class));\n\t}\n\n\t@Test // DATAKV-330\n\tvoid deleteAllById() {\n\n\t\trepo.deleteAllById(Arrays.asList(\"one\", \"two\"));\n\n\t\tverify(opsMock, times(1)).delete(eq(\"one\"), eq(Foo.class));\n\t\tverify(opsMock, times(1)).delete(eq(\"two\"), eq(Foo.class));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid deleteAll() {\n\n\t\trepo.deleteAll();\n\n\t\tverify(opsMock, times(1)).delete(eq(Foo.class));\n\t}\n\n\t@Test // DATACMNS-525\n\t@SuppressWarnings(\"unchecked\")\n\tvoid findAllIds() {\n\n\t\twhen(opsMock.findById(any(), any(Class.class))).thenReturn(Optional.empty());\n\t\trepo.findAllById(Arrays.asList(\"one\", \"two\", \"three\"));\n\n\t\tverify(opsMock, times(3)).findById(anyString(), eq(Foo.class));\n\t}\n\n\t@Test // DATAKV-186\n\t@SuppressWarnings(\"unchecked\")\n\tvoid existsByIdReturnsFalseForEmptyOptional() {\n\n\t\twhen(opsMock.findById(any(), any(Class.class))).thenReturn(Optional.empty());\n\t\tassertThat(repo.existsById(\"one\")).isFalse();\n\t}\n\n\t@Test // DATAKV-186\n\t@SuppressWarnings(\"unchecked\")\n\tvoid existsByIdReturnsTrueWhenOptionalValuePresent() {\n\n\t\twhen(opsMock.findById(any(), any(Class.class))).thenReturn(Optional.of(new Foo()));\n\t\tassertThat(repo.existsById(\"one\")).isTrue();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid findAllWithPageableShouldDelegateToOperationsCorrectlyWhenPageableDoesNotContainSort() {\n\n\t\trepo.findAll(PageRequest.of(10, 15));\n\n\t\tverify(opsMock, times(1)).findInRange(eq(150L), eq(15), eq(Sort.unsorted()), eq(Foo.class));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid findAllWithPageableShouldDelegateToOperationsCorrectlyWhenPageableContainsSort() {\n\n\t\tSort sort = Sort.by(\"for\", \"bar\");\n\t\trepo.findAll(PageRequest.of(10, 15, sort));\n\n\t\tverify(opsMock, times(1)).findInRange(eq(150L), eq(15), eq(sort), eq(Foo.class));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid findAllShouldFallbackToFindAllOfWhenGivenNullPageable() {\n\n\t\trepo.findAll(Pageable.unpaged());\n\n\t\tverify(opsMock, times(1)).findAll(eq(Foo.class));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate <T, S> EntityInformation<T, S> getEntityInformationFor(Class<T> type) {\n\n\t\tPersistentEntity<T, ?> requiredPersistentEntity = (PersistentEntity<T, ?>) context\n\t\t\t\t.getRequiredPersistentEntity(type);\n\n\t\treturn new PersistentEntityInformation<>(requiredPersistentEntity);\n\t}\n\n\tstatic class Foo {\n\n\t\tprivate @Id String id;\n\t\tprivate Long longValue;\n\t\tprivate String name;\n\t\tprivate Bar bar;\n\n\t\tFoo(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\n\t\tpublic Foo() {}\n\n\t\tpublic String getId() {\n\t\t\treturn this.id;\n\t\t}\n\n\t\tpublic Long getLongValue() {\n\t\t\treturn this.longValue;\n\t\t}\n\n\t\tpublic String getName() {\n\t\t\treturn this.name;\n\t\t}\n\n\t\tpublic Bar getBar() {\n\t\t\treturn this.bar;\n\t\t}\n\n\t\tpublic void setId(String id) {\n\t\t\tthis.id = id;\n\t\t}\n\n\t\tpublic void setLongValue(Long longValue) {\n\t\t\tthis.longValue = longValue;\n\t\t}\n\n\t\tpublic void setName(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\n\t\tpublic void setBar(Bar bar) {\n\t\t\tthis.bar = bar;\n\t\t}\n\t}\n\n\tprivate static class Bar {\n\n\t\tprivate String bar;\n\n\t\tpublic String getBar() {\n\t\t\treturn this.bar;\n\t\t}\n\n\t\tpublic void setBar(String bar) {\n\t\t\tthis.bar = bar;\n\t\t}\n\t}\n\n\t@Persistent\n\tstatic class WithNumericId {\n\n\t\t@Id Integer id;\n\t}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/repository/query/AbstractQueryCreatorTestBase.java",
    "content": "/*\n * Copyright 2024-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.repository.query;\n\nimport static org.assertj.core.api.Assertions.*;\nimport static org.mockito.Mockito.*;\n\nimport java.lang.reflect.Method;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.mockito.junit.jupiter.MockitoSettings;\nimport org.mockito.quality.Strictness;\n\nimport org.springframework.dao.InvalidDataAccessApiUsageException;\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.core.TypeInformation;\nimport org.springframework.data.keyvalue.core.query.KeyValueQuery;\nimport org.springframework.data.projection.SpelAwareProxyProjectionFactory;\nimport org.springframework.data.repository.CrudRepository;\nimport org.springframework.data.repository.core.RepositoryMetadata;\nimport org.springframework.data.repository.query.ParametersParameterAccessor;\nimport org.springframework.data.repository.query.QueryMethod;\nimport org.springframework.data.repository.query.parser.AbstractQueryCreator;\nimport org.springframework.data.repository.query.parser.PartTree;\nimport org.springframework.util.ObjectUtils;\n\n/**\n * @author Christoph Strobl\n * @author Tom Van Wemmel\n */\n@ExtendWith(MockitoExtension.class)\n@MockitoSettings(strictness = Strictness.LENIENT)\npublic abstract class AbstractQueryCreatorTestBase<QUERY_CREATOR extends AbstractQueryCreator<KeyValueQuery<CRITERIA>, ?>, CRITERIA> {\n\n\tstatic final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_ZONED_DATE_TIME;\n\n\tstatic final Person RICKON = new Person(\"rickon\", 4);\n\tstatic final Person BRAN = new Person(\"bran\", 9)//\n\t\t\t.skinChanger(true).bornAt(Date.from(ZonedDateTime.parse(\"2013-01-31T06:00:00Z\", FORMATTER).toInstant()));\n\tstatic final Person ARYA = new Person(\"arya\", 13);\n\tstatic final Person ROBB = new Person(\"robb\", 16)//\n\t\t\t.named(\"stark\").bornAt(Date.from(ZonedDateTime.parse(\"2010-09-20T06:00:00Z\", FORMATTER).toInstant()));\n\tstatic final Person JON = new Person(\"jon\", 17).named(\"snow\");\n\n\t@Mock RepositoryMetadata metadataMock;\n\n\t@Test // DATACMNS-525\n\tvoid equalsReturnsTrueWhenMatching() {\n\t\tassertThat(evaluate(\"findByFirstname\", BRAN.firstname).against(BRAN)).isTrue();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid equalsReturnsFalseWhenNotMatching() {\n\t\tassertThat(evaluate(\"findByFirstname\", BRAN.firstname).against(RICKON)).isFalse();\n\t}\n\n\t@Test // GH-603\n\tvoid notEqualsReturnsTrueWhenMatching() {\n\t\tassertThat(evaluate(\"findByFirstnameNot\", BRAN.firstname).against(RICKON)).isTrue();\n\t}\n\n\t@Test // GH-603\n\tvoid notEqualsReturnsFalseWhenNotMatching() {\n\t\tassertThat(evaluate(\"findByFirstnameNot\", BRAN.firstname).against(BRAN)).isFalse();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid isTrueAssertedProperlyWhenTrue() {\n\t\tassertThat(evaluate(\"findBySkinChangerIsTrue\").against(BRAN)).isTrue();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid isTrueAssertedProperlyWhenFalse() {\n\t\tassertThat(evaluate(\"findBySkinChangerIsTrue\").against(RICKON)).isFalse();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid isFalseAssertedProperlyWhenTrue() {\n\t\tassertThat(evaluate(\"findBySkinChangerIsFalse\").against(BRAN)).isFalse();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid isFalseAssertedProperlyWhenFalse() {\n\t\tassertThat(evaluate(\"findBySkinChangerIsFalse\").against(RICKON)).isTrue();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid isNullAssertedProperlyWhenAttributeIsNull() {\n\t\tassertThat(evaluate(\"findByLastnameIsNull\").against(BRAN)).isTrue();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid isNullAssertedProperlyWhenAttributeIsNotNull() {\n\t\tassertThat(evaluate(\"findByLastnameIsNull\").against(ROBB)).isFalse();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid isNotNullFalseTrueWhenAttributeIsNull() {\n\t\tassertThat(evaluate(\"findByLastnameIsNotNull\").against(BRAN)).isFalse();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid isNotNullReturnsTrueAttributeIsNotNull() {\n\t\tassertThat(evaluate(\"findByLastnameIsNotNull\").against(ROBB)).isTrue();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid startsWithReturnsTrueWhenMatching() {\n\t\tassertThat(evaluate(\"findByFirstnameStartingWith\", \"r\").against(ROBB)).isTrue();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid startsWithReturnsFalseWhenNotMatching() {\n\t\tassertThat(evaluate(\"findByFirstnameStartingWith\", \"r\").against(BRAN)).isFalse();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid likeReturnsTrueWhenMatching() {\n\t\tassertThat(evaluate(\"findByFirstnameLike\", \"ob\").against(ROBB)).isTrue();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid likeReturnsFalseWhenNotMatching() {\n\t\tassertThat(evaluate(\"findByFirstnameLike\", \"ra\").against(ROBB)).isFalse();\n\t}\n\n\t@Test // GH-603\n\tvoid notLikeReturnsTrueWhenMatching() {\n\t\tassertThat(evaluate(\"findByFirstnameNotLike\", \"ra\").against(ROBB)).isTrue();\n\t}\n\n\t@Test // GH-603\n\tvoid notLikeReturnsFalseWhenNotMatching() {\n\t\tassertThat(evaluate(\"findByFirstnameNotLike\", \"ob\").against(ROBB)).isFalse();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid endsWithReturnsTrueWhenMatching() {\n\t\tassertThat(evaluate(\"findByFirstnameEndingWith\", \"bb\").against(ROBB)).isTrue();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid endsWithReturnsFalseWhenNotMatching() {\n\t\tassertThat(evaluate(\"findByFirstnameEndingWith\", \"an\").against(ROBB)).isFalse();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid startsWithIgnoreCaseReturnsTrueWhenMatching() {\n\t\tassertThatExceptionOfType(InvalidDataAccessApiUsageException.class)\n\t\t\t\t.isThrownBy(() -> evaluate(\"findByFirstnameIgnoreCase\", \"R\").against(ROBB));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid greaterThanReturnsTrueForHigherValues() {\n\t\tassertThat(evaluate(\"findByAgeGreaterThan\", BRAN.age).against(ROBB)).isTrue();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid greaterThanReturnsFalseForLowerValues() {\n\t\tassertThat(evaluate(\"findByAgeGreaterThan\", BRAN.age).against(RICKON)).isFalse();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid afterReturnsTrueForHigherValues() {\n\t\tassertThat(evaluate(\"findByBirthdayAfter\", ROBB.birthday).against(BRAN)).isTrue();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid afterReturnsFalseForLowerValues() {\n\t\tassertThat(evaluate(\"findByBirthdayAfter\", BRAN.birthday).against(ROBB)).isFalse();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid greaterThanEaualsReturnsTrueForHigherValues() {\n\t\tassertThat(evaluate(\"findByAgeGreaterThanEqual\", BRAN.age).against(ROBB)).isTrue();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid greaterThanEqualsReturnsTrueForEqualValues() {\n\t\tassertThat(evaluate(\"findByAgeGreaterThanEqual\", BRAN.age).against(BRAN)).isTrue();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid greaterThanEqualsReturnsFalseForLowerValues() {\n\t\tassertThat(evaluate(\"findByAgeGreaterThanEqual\", BRAN.age).against(RICKON)).isFalse();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid lessThanReturnsTrueForHigherValues() {\n\t\tassertThat(evaluate(\"findByAgeLessThan\", BRAN.age).against(ROBB)).isFalse();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid lessThanReturnsFalseForLowerValues() {\n\t\tassertThat(evaluate(\"findByAgeLessThan\", BRAN.age).against(RICKON)).isTrue();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid beforeReturnsTrueForLowerValues() {\n\t\tassertThat(evaluate(\"findByBirthdayBefore\", BRAN.birthday).against(ROBB)).isTrue();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid beforeReturnsFalseForHigherValues() {\n\t\tassertThat(evaluate(\"findByBirthdayBefore\", ROBB.birthday).against(BRAN)).isFalse();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid lessThanEaualsReturnsTrueForHigherValues() {\n\t\tassertThat(evaluate(\"findByAgeLessThanEqual\", BRAN.age).against(ROBB)).isFalse();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid lessThanEaualsReturnsTrueForEqualValues() {\n\t\tassertThat(evaluate(\"findByAgeLessThanEqual\", BRAN.age).against(BRAN)).isTrue();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid lessThanEqualsReturnsFalseForLowerValues() {\n\t\tassertThat(evaluate(\"findByAgeLessThanEqual\", BRAN.age).against(RICKON)).isTrue();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid betweenEqualsReturnsTrueForValuesInBetween() {\n\t\tassertThat(evaluate(\"findByAgeBetween\", BRAN.age, ROBB.age).against(ARYA)).isTrue();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid betweenEqualsReturnsFalseForHigherValues() {\n\t\tassertThat(evaluate(\"findByAgeBetween\", BRAN.age, ROBB.age).against(JON)).isFalse();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid betweenEqualsReturnsFalseForLowerValues() {\n\t\tassertThat(evaluate(\"findByAgeBetween\", BRAN.age, ROBB.age).against(RICKON)).isFalse();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid connectByAndReturnsTrueWhenAllPropertiesMatching() {\n\t\tassertThat(evaluate(\"findByAgeGreaterThanAndLastname\", BRAN.age, JON.lastname).against(JON)).isTrue();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid connectByAndReturnsFalseWhenOnlyFewPropertiesMatch() {\n\t\tassertThat(evaluate(\"findByAgeGreaterThanAndLastname\", BRAN.age, JON.lastname).against(ROBB)).isFalse();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid connectByOrReturnsTrueWhenOnlyFewPropertiesMatch() {\n\t\tassertThat(evaluate(\"findByAgeGreaterThanOrLastname\", BRAN.age, JON.lastname).against(ROBB)).isTrue();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid connectByOrReturnsTrueWhenAllPropertiesMatch() {\n\t\tassertThat(evaluate(\"findByAgeGreaterThanOrLastname\", BRAN.age, JON.lastname).against(JON)).isTrue();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid regexReturnsTrueWhenMatching() {\n\t\tassertThat(evaluate(\"findByLastnameMatches\", \"^s.*w$\").against(JON)).isTrue();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid regexReturnsFalseWhenNotMatching() {\n\t\tassertThat(evaluate(\"findByLastnameMatches\", \"^s.*w$\").against(ROBB)).isFalse();\n\t}\n\n\t@Test // DATAKV-169\n\tvoid inReturnsMatchCorrectly() {\n\n\t\tList<String> list = new ArrayList<>();\n\t\tlist.add(ROBB.firstname);\n\n\t\tassertThat(evaluate(\"findByFirstnameIn\", list).against(ROBB)).isTrue();\n\t}\n\n\t@Test // DATAKV-169\n\tvoid inNotMatchingReturnsCorrectly() {\n\n\t\tList<String> list = new ArrayList<>();\n\t\tlist.add(ROBB.firstname);\n\n\t\tassertThat(evaluate(\"findByFirstnameIn\", list).against(JON)).isFalse();\n\t}\n\n\t@Test // DATAKV-169\n\tvoid inWithNullCompareValuesCorrectly() {\n\n\t\tList<String> list = new ArrayList<>();\n\t\tlist.add(null);\n\n\t\tassertThat(evaluate(\"findByFirstnameIn\", list).against(JON)).isFalse();\n\t}\n\n\t@Test // DATAKV-169\n\tvoid inWithNullSourceValuesMatchesCorrectly() {\n\n\t\tList<String> list = new ArrayList<>();\n\t\tlist.add(ROBB.firstname);\n\n\t\tassertThat(evaluate(\"findByFirstnameIn\", list).against(new PredicateQueryCreatorUnitTests.Person(null, 10)))\n\t\t\t\t.isFalse();\n\t}\n\n\t@Test // DATAKV-169\n\tvoid inMatchesNullValuesCorrectly() {\n\n\t\tList<String> list = new ArrayList<>();\n\t\tlist.add(null);\n\n\t\tboolean contains = list.contains(null);\n\n\t\tassertThat(evaluate(\"findByFirstnameIn\", list).against(new PredicateQueryCreatorUnitTests.Person(null, 10)))\n\t\t\t\t.isTrue();\n\t}\n\n\t@Test // GH-603\n\tvoid notInReturnsMatchCorrectly() {\n\n\t\tList<String> list = new ArrayList<>();\n\t\tlist.add(ROBB.firstname);\n\n\t\tassertThat(evaluate(\"findByFirstnameNotIn\", list).against(JON)).isTrue();\n\t}\n\n\t@Test // GH-603\n\tvoid notInNotMatchingReturnsCorrectly() {\n\n\t\tList<String> list = new ArrayList<>();\n\t\tlist.add(ROBB.firstname);\n\n\t\tassertThat(evaluate(\"findByFirstnameNotIn\", list).against(ROBB)).isFalse();\n\t}\n\n\t@Test // GH-603\n\tvoid notInWithNullCompareValuesCorrectly() {\n\n\t\tList<String> list = new ArrayList<>();\n\t\tlist.add(null);\n\n\t\tassertThat(evaluate(\"findByFirstnameNotIn\", list).against(JON)).isTrue();\n\t}\n\n\t@Test // GH-603\n\tvoid notInWithNullSourceValuesMatchesCorrectly() {\n\n\t\tList<String> list = new ArrayList<>();\n\t\tlist.add(ROBB.firstname);\n\n\t\tassertThat(evaluate(\"findByFirstnameNotIn\", list).against(new PredicateQueryCreatorUnitTests.Person(null, 10)))\n\t\t\t\t.isTrue();\n\t}\n\n\t@Test // GH-603\n\tvoid notInMatchesNullValuesCorrectly() {\n\n\t\tList<String> list = new ArrayList<>();\n\t\tlist.add(null);\n\n\t\tassertThat(evaluate(\"findByFirstnameNotIn\", list).against(new PredicateQueryCreatorUnitTests.Person(null, 10)))\n\t\t\t\t.isFalse();\n\t}\n\n\t@Test // DATAKV-185\n\tvoid noDerivedQueryArgumentsMatchesAlways() {\n\n\t\tassertThat(evaluate(\"findBy\").against(JON)).isTrue();\n\t\tassertThat(evaluate(\"findBy\").against(null)).isTrue();\n\t}\n\n\tprotected Evaluation evaluate(String methodName, Object... args) {\n\t\ttry {\n\t\t\treturn createEvaluation(createQueryForMethodWithArgs(methodName, args).getCriteria());\n\t\t} catch (ReflectiveOperationException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\tprotected abstract Evaluation createEvaluation(CRITERIA criteria);\n\n\tprotected KeyValueQuery<CRITERIA> createQueryForMethodWithArgs(String methodName, Object... args)\n\t\t\tthrows NoSuchMethodException, SecurityException {\n\n\t\tClass<?>[] argTypes = new Class<?>[args.length];\n\t\tif (!ObjectUtils.isEmpty(args)) {\n\n\t\t\tfor (int i = 0; i < args.length; i++) {\n\t\t\t\targTypes[i] = args[i].getClass();\n\t\t\t}\n\t\t}\n\n\t\tMethod method = getMethod(PersonRepository.class, methodName, argTypes);\n\t\tdoReturn(Person.class).when(metadataMock).getReturnedDomainClass(method);\n\t\tdoReturn(TypeInformation.of(Person.class)).when(metadataMock).getDomainTypeInformation();\n\t\tdoReturn(TypeInformation.of(Person.class)).when(metadataMock).getReturnType(method);\n\n\t\tPartTree partTree = new PartTree(method.getName(), method.getReturnType());\n\t\tQUERY_CREATOR creator = queryCreator(partTree, new ParametersParameterAccessor(\n\t\t\t\tnew QueryMethod(method, metadataMock, new SpelAwareProxyProjectionFactory()).getParameters(), args));\n\n\t\tKeyValueQuery<CRITERIA> q = creator.createQuery();\n\t\treturn finalizeQuery(q, args);\n\t}\n\n\tprivate Method getMethod(Class<?> type, String methodName, Class<?>[] argTypes) throws NoSuchMethodException {\n\n\t\tfor (Method declaredMethod : type.getDeclaredMethods()) {\n\n\t\t\tif (!declaredMethod.getName().equals(methodName)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (declaredMethod.getParameterCount() != argTypes.length) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tClass<?>[] types = declaredMethod.getParameterTypes();\n\n\t\t\tboolean assigable = true;\n\t\t\tfor (int i = 0; i < types.length; i++) {\n\n\t\t\t\tif (!types[i].isAssignableFrom(argTypes[i])) {\n\t\t\t\t\tassigable = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (assigable) {\n\t\t\t\treturn declaredMethod;\n\t\t\t}\n\t\t}\n\n\t\tthrow new NoSuchMethodException(\"Method \" + methodName + \" not found in \" + type);\n\t}\n\n\tprotected abstract QUERY_CREATOR queryCreator(PartTree partTree, ParametersParameterAccessor accessor);\n\n\tprotected abstract KeyValueQuery<CRITERIA> finalizeQuery(KeyValueQuery<CRITERIA> query, Object... args);\n\n\tinterface PersonRepository extends CrudRepository<Person, String> {\n\n\t\t// No arguments\n\t\tPerson findBy();\n\n\t\t// Type.SIMPLE_PROPERTY\n\t\tPerson findByFirstname(String firstname);\n\n\t\t// Type.NEGATING_SIMPLE_PROPERTY\n\t\tPerson findByFirstnameNot(String firstname);\n\n\t\t// Type.TRUE\n\t\tPerson findBySkinChangerIsTrue();\n\n\t\t// Type.FALSE\n\t\tPerson findBySkinChangerIsFalse();\n\n\t\t// Type.IS_NULL\n\t\tPerson findByLastnameIsNull();\n\n\t\t// Type.IS_NOT_NULL\n\t\tPerson findByLastnameIsNotNull();\n\n\t\t// Type.STARTING_WITH\n\t\tPerson findByFirstnameStartingWith(String firstanme);\n\n\t\tPerson findByFirstnameIgnoreCase(String firstanme);\n\n\t\t// Type.AFTER\n\t\tPerson findByBirthdayAfter(Date date);\n\n\t\t// Type.GREATHER_THAN\n\t\tPerson findByAgeGreaterThan(Integer age);\n\n\t\t// Type.GREATER_THAN_EQUAL\n\t\tPerson findByAgeGreaterThanEqual(Integer age);\n\n\t\t// Type.BEFORE\n\t\tPerson findByBirthdayBefore(Date date);\n\n\t\t// Type.LESS_THAN\n\t\tPerson findByAgeLessThan(Integer age);\n\n\t\t// Type.LESS_THAN_EQUAL\n\t\tPerson findByAgeLessThanEqual(Integer age);\n\n\t\t// Type.BETWEEN\n\t\tPerson findByAgeBetween(Integer low, Integer high);\n\n\t\t// Type.LIKE\n\t\tPerson findByFirstnameLike(String firstname);\n\n\t\t// Type.NOT_LIKE\n\t\tPerson findByFirstnameNotLike(String firstname);\n\n\t\t// Type.ENDING_WITH\n\t\tPerson findByFirstnameEndingWith(String firstname);\n\n\t\tPerson findByAgeGreaterThanAndLastname(Integer age, String lastname);\n\n\t\tPerson findByAgeGreaterThanOrLastname(Integer age, String lastname);\n\n\t\t// Type.REGEX\n\t\tPerson findByLastnameMatches(String lastname);\n\n\t\t// Type.IN\n\t\tPerson findByFirstnameIn(List<String> in);\n\n\t\t// Type.NOT_IN\n\t\tPerson findByFirstnameNotIn(List<String> in);\n\n\t}\n\n\tpublic interface Evaluation {\n\t\tBoolean against(Object candidate);\n\n\t\tboolean evaluate();\n\t}\n\n\tpublic static class Person {\n\n\t\tprivate @Id String id;\n\t\tprivate String firstname, lastname;\n\t\tprivate int age;\n\t\tprivate boolean isSkinChanger = false;\n\t\tprivate Date birthday;\n\n\t\tpublic Person() {}\n\n\t\tPerson(String firstname, int age) {\n\t\t\tsuper();\n\t\t\tthis.firstname = firstname;\n\t\t\tthis.age = age;\n\t\t}\n\n\t\tPerson skinChanger(boolean isSkinChanger) {\n\t\t\tthis.isSkinChanger = isSkinChanger;\n\t\t\treturn this;\n\t\t}\n\n\t\tPerson named(String lastname) {\n\t\t\tthis.lastname = lastname;\n\t\t\treturn this;\n\t\t}\n\n\t\tPerson bornAt(Date date) {\n\t\t\tthis.birthday = date;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic String getId() {\n\t\t\treturn this.id;\n\t\t}\n\n\t\tpublic String getFirstname() {\n\t\t\treturn this.firstname;\n\t\t}\n\n\t\tpublic String getLastname() {\n\t\t\treturn this.lastname;\n\t\t}\n\n\t\tpublic int getAge() {\n\t\t\treturn this.age;\n\t\t}\n\n\t\tpublic boolean isSkinChanger() {\n\t\t\treturn this.isSkinChanger;\n\t\t}\n\n\t\tpublic Date getBirthday() {\n\t\t\treturn this.birthday;\n\t\t}\n\n\t\tpublic void setId(String id) {\n\t\t\tthis.id = id;\n\t\t}\n\n\t\tpublic void setFirstname(String firstname) {\n\t\t\tthis.firstname = firstname;\n\t\t}\n\n\t\tpublic void setLastname(String lastname) {\n\t\t\tthis.lastname = lastname;\n\t\t}\n\n\t\tpublic void setAge(int age) {\n\t\t\tthis.age = age;\n\t\t}\n\n\t\tpublic void setSkinChanger(boolean isSkinChanger) {\n\t\t\tthis.isSkinChanger = isSkinChanger;\n\t\t}\n\n\t\tpublic void setBirthday(Date birthday) {\n\t\t\tthis.birthday = birthday;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/repository/query/CachingKeyValuePartTreeQueryUnitTests.java",
    "content": "/*\n * Copyright 2016-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.repository.query;\n\nimport static org.assertj.core.api.Assertions.*;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.data.core.TypeInformation;\nimport org.springframework.data.keyvalue.Person;\nimport org.springframework.data.keyvalue.core.KeyValueOperations;\nimport org.springframework.data.keyvalue.core.SpelCriteria;\nimport org.springframework.data.projection.ProjectionFactory;\nimport org.springframework.data.repository.core.RepositoryMetadata;\nimport org.springframework.data.repository.query.QueryMethod;\nimport org.springframework.data.repository.query.ValueExpressionDelegate;\n\n/**\n * Unit tests for {@link CachingKeyValuePartTreeQuery}.\n *\n * @author Mark Paluch\n */\n@ExtendWith(MockitoExtension.class)\nclass CachingKeyValuePartTreeQueryUnitTests {\n\n\t@Mock KeyValueOperations kvOpsMock;\n\t@Mock RepositoryMetadata metadataMock;\n\t@Mock ProjectionFactory projectionFactoryMock;\n\n\t@BeforeEach\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\tvoid setUp() throws Exception {\n\n\t\twhen(metadataMock.getDomainType()).thenReturn((Class) Person.class);\n\t\twhen(metadataMock.getDomainTypeInformation()).thenReturn((TypeInformation) TypeInformation.of(Person.class));\n\t\twhen(metadataMock.getReturnedDomainClass(any(Method.class))).thenReturn((Class) Person.class);\n\t\twhen(metadataMock.getReturnType(any(Method.class))).thenReturn(TypeInformation.of((Class) List.class));\n\t}\n\n\t@Test // DATAKV-137\n\tvoid cachedSpelExpressionShouldBeReusedWithNewContext() throws NoSuchMethodException, SecurityException {\n\n\t\tQueryMethod qm = new QueryMethod(Repo.class.getMethod(\"findByFirstname\", String.class), metadataMock,\n\t\t\t\tprojectionFactoryMock);\n\n\t\tKeyValuePartTreeQuery query = new CachingKeyValuePartTreeQuery(qm, ValueExpressionDelegate.create(), kvOpsMock,\n\t\t\t\tSpelQueryCreator.class);\n\n\t\tObject[] args = new Object[] { \"foo\" };\n\n\t\tSpelCriteria first = (SpelCriteria) query.prepareQuery(args).getCriteria();\n\t\tSpelCriteria second = (SpelCriteria) query.prepareQuery(args).getCriteria();\n\n\t\tassertThat(first.getExpression()).isSameAs(second.getExpression());\n\t\tassertThat(first.getContext()).isNotSameAs(second.getContext());\n\t}\n\n\tstatic interface Repo {\n\n\t\tList<Person> findByFirstname(String firstname);\n\t}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/repository/query/KeyValuePartTreeQueryUnitTests.java",
    "content": "/*\n * Copyright 2015-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.repository.query;\n\nimport static org.assertj.core.api.Assertions.*;\nimport static org.mockito.ArgumentMatchers.*;\nimport static org.mockito.Mockito.*;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.data.core.TypeInformation;\nimport org.springframework.data.domain.PageRequest;\nimport org.springframework.data.domain.Pageable;\nimport org.springframework.data.keyvalue.Person;\nimport org.springframework.data.keyvalue.core.KeyValueOperations;\nimport org.springframework.data.keyvalue.core.SpelCriteria;\nimport org.springframework.data.keyvalue.core.query.KeyValueQuery;\nimport org.springframework.data.projection.ProjectionFactory;\nimport org.springframework.data.repository.core.RepositoryMetadata;\nimport org.springframework.data.repository.query.QueryMethod;\nimport org.springframework.data.repository.query.ValueExpressionDelegate;\n\n/**\n * Unit tests for {@link KeyValuePartTreeQuery}.\n *\n * @author Christoph Strobl\n * @author Mark Paluch\n */\n@ExtendWith(MockitoExtension.class)\nclass KeyValuePartTreeQueryUnitTests {\n\n\t@Mock KeyValueOperations kvOpsMock;\n\t@Mock RepositoryMetadata metadataMock;\n\t@Mock ProjectionFactory projectionFactoryMock;\n\n\t@Test // DATAKV-115\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\tvoid spelExpressionAndContextShouldNotBeReused() throws NoSuchMethodException, SecurityException {\n\n\t\twhen(metadataMock.getDomainType()).thenReturn((Class) Person.class);\n\t\twhen(metadataMock.getDomainTypeInformation()).thenReturn((TypeInformation) TypeInformation.of(Person.class));\n\t\twhen(metadataMock.getReturnType(any(Method.class))).thenReturn((TypeInformation) TypeInformation.of(List.class));\n\t\twhen(metadataMock.getReturnedDomainClass(any(Method.class))).thenReturn((Class) Person.class);\n\n\t\tQueryMethod qm = new QueryMethod(Repo.class.getMethod(\"findByFirstname\", String.class), metadataMock,\n\t\t\t\tprojectionFactoryMock);\n\n\t\tKeyValuePartTreeQuery query = new KeyValuePartTreeQuery(qm, ValueExpressionDelegate.create(), kvOpsMock,\n\t\t\t\tSpelQueryCreator.class);\n\n\t\tObject[] args = new Object[] { \"foo\" };\n\n\t\tObject first = query.prepareQuery(args).getCriteria();\n\t\tObject second = query.prepareQuery(args).getCriteria();\n\n\t\tassertThat(first).isNotSameAs(second);\n\t}\n\n\t@Test // DATAKV-142\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\tvoid shouldApplyPageableParameterToCollectionQuery() throws SecurityException, NoSuchMethodException {\n\n\t\twhen(metadataMock.getDomainType()).thenReturn((Class) Person.class);\n\t\twhen(metadataMock.getDomainTypeInformation()).thenReturn((TypeInformation) TypeInformation.of(Person.class));\n\t\twhen(metadataMock.getReturnType(any(Method.class))).thenReturn((TypeInformation) TypeInformation.of(List.class));\n\t\twhen(metadataMock.getReturnedDomainClass(any(Method.class))).thenReturn((Class) Person.class);\n\n\t\tQueryMethod qm = new QueryMethod(Repo.class.getMethod(\"findBy\", Pageable.class), metadataMock,\n\t\t\t\tprojectionFactoryMock);\n\n\t\tKeyValuePartTreeQuery partTreeQuery = new KeyValuePartTreeQuery(qm, ValueExpressionDelegate.create(), kvOpsMock,\n\t\t\t\tSpelQueryCreator.class);\n\n\t\tKeyValueQuery<?> query = partTreeQuery.prepareQuery(new Object[] { PageRequest.of(2, 3) });\n\n\t\tassertThat(query.getOffset()).isEqualTo(6L);\n\t\tassertThat(query.getRows()).isEqualTo(3);\n\t}\n\n\t@Test // GH-563\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\tvoid shouldAllowProjectionQueries() throws SecurityException, NoSuchMethodException {\n\n\t\twhen(metadataMock.getDomainType()).thenReturn((Class) Person.class);\n\t\twhen(metadataMock.getDomainTypeInformation()).thenReturn((TypeInformation) TypeInformation.of(Person.class));\n\t\twhen(metadataMock.getReturnType(any(Method.class))).thenReturn((TypeInformation) TypeInformation.of(List.class));\n\t\twhen(metadataMock.getReturnedDomainClass(any(Method.class))).thenReturn((Class) Person.class);\n\n\t\tQueryMethod qm = new QueryMethod(Repo.class.getMethod(\"findProjectionByFirstname\", String.class), metadataMock,\n\t\t\t\tprojectionFactoryMock);\n\n\t\tKeyValuePartTreeQuery partTreeQuery = new KeyValuePartTreeQuery(qm, ValueExpressionDelegate.create(), kvOpsMock,\n\t\t\t\tSpelQueryCreator.class);\n\n\t\tKeyValueQuery<?> query = partTreeQuery.prepareQuery(new Object[] { \"firstname\" });\n\t\tpartTreeQuery.doExecute(new Object[] { \"firstname\" }, query);\n\n\t\tverify(kvOpsMock).find(eq(query), eq(Person.class));\n\t}\n\n\t@Test // DATAKV-142\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\tvoid shouldApplyDerivedMaxResultsToQuery() throws SecurityException, NoSuchMethodException {\n\n\t\twhen(metadataMock.getDomainType()).thenReturn((Class) Person.class);\n\t\twhen(metadataMock.getReturnType(any(Method.class))).thenReturn((TypeInformation) TypeInformation.of(List.class));\n\t\twhen(metadataMock.getReturnedDomainClass(any(Method.class))).thenReturn((Class) Person.class);\n\n\t\tQueryMethod qm = new QueryMethod(Repo.class.getMethod(\"findTop3By\"), metadataMock, projectionFactoryMock);\n\n\t\tKeyValuePartTreeQuery partTreeQuery = new KeyValuePartTreeQuery(qm, ValueExpressionDelegate.create(), kvOpsMock,\n\t\t\t\tSpelQueryCreator.class);\n\n\t\tKeyValueQuery<?> query = partTreeQuery.prepareQuery(new Object[] {});\n\n\t\tassertThat(query.getRows()).isEqualTo(3);\n\t}\n\n\t@Test // DATAKV-142\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\tvoid shouldApplyDerivedMaxResultsToQueryWithParameters() throws SecurityException, NoSuchMethodException {\n\n\t\twhen(metadataMock.getDomainType()).thenReturn((Class) Person.class);\n\t\twhen(metadataMock.getDomainTypeInformation()).thenReturn((TypeInformation) TypeInformation.of(Person.class));\n\t\twhen(metadataMock.getReturnType(any(Method.class))).thenReturn((TypeInformation) TypeInformation.of(List.class));\n\t\twhen(metadataMock.getReturnedDomainClass(any(Method.class))).thenReturn((Class) Person.class);\n\n\t\tQueryMethod qm = new QueryMethod(Repo.class.getMethod(\"findTop3ByFirstname\", String.class), metadataMock,\n\t\t\t\tprojectionFactoryMock);\n\n\t\tKeyValuePartTreeQuery partTreeQuery = new KeyValuePartTreeQuery(qm, ValueExpressionDelegate.create(), kvOpsMock,\n\t\t\t\tSpelQueryCreator.class);\n\n\t\tKeyValueQuery<?> query = partTreeQuery.prepareQuery(new Object[] { \"firstname\" });\n\n\t\tassertThat(query.getCriteria()).isInstanceOf(SpelCriteria.class);\n\t\tassertThat(((SpelCriteria) query.getCriteria()).getExpression().getExpressionString())\n\t\t\t\t.isEqualTo(\"#it?.firstname?.equals([0])\");\n\t\tassertThat(query.getRows()).isEqualTo(3);\n\t}\n\n\t@Test // GH-385\n\tvoid shouldUseCountForExists() throws NoSuchMethodException {\n\n\t\twhen(metadataMock.getDomainType()).thenReturn((Class) Person.class);\n\t\twhen(metadataMock.getDomainTypeInformation()).thenReturn((TypeInformation) TypeInformation.of(Person.class));\n\t\twhen(metadataMock.getReturnType(any(Method.class))).thenReturn((TypeInformation) TypeInformation.of(Boolean.class));\n\t\twhen(metadataMock.getReturnedDomainClass(any(Method.class))).thenReturn((Class) Boolean.class);\n\n\t\tQueryMethod qm = new QueryMethod(Repo.class.getMethod(\"existsByFirstname\", String.class), metadataMock,\n\t\t\t\tprojectionFactoryMock);\n\n\t\tKeyValuePartTreeQuery partTreeQuery = new KeyValuePartTreeQuery(qm, ValueExpressionDelegate.create(), kvOpsMock,\n\t\t\t\tSpelQueryCreator.class);\n\n\t\tKeyValueQuery<?> query = partTreeQuery.prepareQuery(new Object[] { \"firstname\" });\n\t\tpartTreeQuery.doExecute(new Object[] { \"firstname\" }, query);\n\n\t\tverify(kvOpsMock).exists(eq(query), eq(Person.class));\n\t}\n\n\t@Test // GH-71\n\tvoid shouldUseCountForCount() throws NoSuchMethodException {\n\n\t\twhen(metadataMock.getDomainType()).thenReturn((Class) Person.class);\n\t\twhen(metadataMock.getDomainTypeInformation()).thenReturn((TypeInformation) TypeInformation.of(Person.class));\n\t\twhen(metadataMock.getReturnType(any(Method.class))).thenReturn((TypeInformation) TypeInformation.of(Boolean.class));\n\t\twhen(metadataMock.getReturnedDomainClass(any(Method.class))).thenReturn((Class) Boolean.class);\n\n\t\tQueryMethod qm = new QueryMethod(Repo.class.getMethod(\"countByFirstname\", String.class), metadataMock,\n\t\t\t\tprojectionFactoryMock);\n\n\t\tKeyValuePartTreeQuery partTreeQuery = new KeyValuePartTreeQuery(qm, ValueExpressionDelegate.create(), kvOpsMock,\n\t\t\t\tSpelQueryCreator.class);\n\n\t\tKeyValueQuery<?> query = partTreeQuery.prepareQuery(new Object[] { \"firstname\" });\n\t\tpartTreeQuery.doExecute(new Object[] { \"firstname\" }, query);\n\n\t\tverify(kvOpsMock).count(eq(query), eq(Person.class));\n\t}\n\n\tinterface Repo {\n\n\t\tList<Person> findByFirstname(String firstname);\n\n\t\tboolean existsByFirstname(String firstname);\n\n\t\tint countByFirstname(String firstname);\n\n\t\tList<Person> findBy(Pageable page);\n\n\t\tList<Person> findTop3By();\n\n\t\tList<Person> findTop3ByFirstname(String firstname);\n\n\t\tPersonProjection findProjectionByFirstname(String firstname);\n\t}\n\n\tinterface PersonProjection {\n\t\tString getFirstname();\n\t}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/repository/query/PredicateQueryCreatorUnitTests.java",
    "content": "/*\n * Copyright 2024-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.repository.query;\n\nimport static org.assertj.core.api.Assertions.*;\n\nimport java.util.function.Predicate;\n\nimport org.junit.jupiter.api.Test;\nimport org.springframework.dao.InvalidDataAccessApiUsageException;\nimport org.springframework.data.keyvalue.core.query.KeyValueQuery;\nimport org.springframework.data.repository.query.ParametersParameterAccessor;\nimport org.springframework.data.repository.query.parser.PartTree;\n\n/**\n * @author Christoph Strobl\n */\nclass PredicateQueryCreatorUnitTests extends AbstractQueryCreatorTestBase<PredicateQueryCreator, Predicate<?>> {\n\n\t@Override\n\t@Test // DATACMNS-525\n\tvoid startsWithIgnoreCaseReturnsTrueWhenMatching() {\n\t\tassertThat(evaluate(\"findByFirstnameIgnoreCase\", \"RobB\").against(ROBB)).isTrue();\n\t}\n\n\t@Override\n\tprotected PredicateQueryCreator queryCreator(PartTree partTree, ParametersParameterAccessor accessor) {\n\t\treturn new PredicateQueryCreator(partTree, accessor);\n\t}\n\n\t@Override\n\tprotected KeyValueQuery<Predicate<?>> finalizeQuery(KeyValueQuery<Predicate<?>> query, Object... args) {\n\t\treturn query;\n\t}\n\n\t@Override\n\tprotected Evaluation createEvaluation(Predicate<?> predicate) {\n\t\treturn new PredicateEvaluation(predicate);\n\t}\n\n\tstatic class PredicateEvaluation implements Evaluation {\n\n\t\tprivate final Predicate expression;\n\t\tprivate Object candidate;\n\n\t\tPredicateEvaluation(Predicate<?> expression) {\n\t\t\tthis.expression = expression;\n\t\t}\n\n\t\tpublic Boolean against(Object candidate) {\n\t\t\tthis.candidate = candidate;\n\t\t\treturn evaluate();\n\t\t}\n\n\t\tpublic boolean evaluate() {\n\t\t\treturn expression.test(candidate);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/repository/query/SpelQueryCreatorUnitTests.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.repository.query;\n\nimport org.springframework.data.keyvalue.core.query.KeyValueQuery;\nimport org.springframework.data.repository.query.ParametersParameterAccessor;\nimport org.springframework.data.repository.query.parser.PartTree;\nimport org.springframework.expression.spel.standard.SpelExpression;\nimport org.springframework.expression.spel.support.SimpleEvaluationContext;\n\n/**\n * @author Christoph Strobl\n * @author Mark Paluch\n */\npublic class SpelQueryCreatorUnitTests extends AbstractQueryCreatorTestBase<SpelQueryCreator, SpelExpression> {\n\n\t@Override\n\tprotected SpelQueryCreator queryCreator(PartTree partTree, ParametersParameterAccessor accessor) {\n\t\treturn new SpelQueryCreator(partTree, accessor);\n\t}\n\n\t@Override\n\tprotected KeyValueQuery<SpelExpression> finalizeQuery(KeyValueQuery<SpelExpression> query, Object... args) {\n\n\t\tquery.getCriteria().setEvaluationContext(\n\t\t\t\tSimpleEvaluationContext.forReadOnlyDataBinding().withRootObject(args).withInstanceMethods().build());\n\t\treturn query;\n\t}\n\n\t@Override\n\tprotected Evaluation createEvaluation(SpelExpression spelExpression) {\n\t\treturn new SpelEvaluation(spelExpression);\n\t}\n\n\tstatic class SpelEvaluation implements Evaluation {\n\n\t\tSpelExpression expression;\n\t\tObject candidate;\n\n\t\tSpelEvaluation(SpelExpression expression) {\n\t\t\tthis.expression = expression;\n\t\t}\n\n\t\tpublic Boolean against(Object candidate) {\n\t\t\tthis.candidate = candidate;\n\t\t\treturn evaluate();\n\t\t}\n\n\t\tpublic boolean evaluate() {\n\t\t\texpression.getEvaluationContext().setVariable(\"it\", candidate);\n\t\t\treturn expression.getValue(Boolean.class);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/repository/support/KeyValueQuerydslUtilsUnitTests.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.repository.support;\n\nimport static org.assertj.core.api.Assertions.*;\nimport static org.springframework.data.keyvalue.repository.support.KeyValueQuerydslUtils.*;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.data.domain.Sort;\nimport org.springframework.data.domain.Sort.Direction;\nimport org.springframework.data.domain.Sort.NullHandling;\nimport org.springframework.data.keyvalue.Person;\nimport org.springframework.data.keyvalue.QPerson;\nimport org.springframework.data.querydsl.SimpleEntityPathResolver;\n\nimport com.querydsl.core.types.EntityPath;\nimport com.querydsl.core.types.OrderSpecifier;\nimport com.querydsl.core.types.dsl.PathBuilder;\n\n/**\n * Unit tests for {@link KeyValueQuerydslUtils}.\n *\n * @author Christoph Strobl\n * @author Thomas Darimont\n * @author Oliver Gierke\n * @author Mark Paluch\n */\nclass KeyValueQuerydslUtilsUnitTests {\n\n\tprivate EntityPath<Person> path;\n\tprivate PathBuilder<Person> builder;\n\n\t@BeforeEach\n\tvoid setUp() {\n\n\t\tthis.path = SimpleEntityPathResolver.INSTANCE.createPath(Person.class);\n\t\tthis.builder = new PathBuilder<>(path.getType(), path.getMetadata());\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid toOrderSpecifierThrowsExceptioOnNullPathBuilder() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> toOrderSpecifier(Sort.by(\"firstname\"), null));\n\t}\n\n\t@Test // DATACMNS-525, DATAKV-197\n\tvoid toOrderSpecifierReturnsEmptyArrayWhenSortIsUnsorted() {\n\t\tassertThat(toOrderSpecifier(Sort.unsorted(), builder)).hasSize(0);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid toOrderSpecifierConvertsSimpleAscSortCorrectly() {\n\n\t\tSort sort = Sort.by(Direction.ASC, \"firstname\");\n\n\t\tOrderSpecifier<?>[] specifiers = toOrderSpecifier(sort, builder);\n\n\t\tassertThat(specifiers).containsExactly(QPerson.person.firstname.asc());\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid toOrderSpecifierConvertsSimpleDescSortCorrectly() {\n\n\t\tSort sort = Sort.by(Direction.DESC, \"firstname\");\n\n\t\tOrderSpecifier<?>[] specifiers = toOrderSpecifier(sort, builder);\n\n\t\tassertThat(specifiers).containsExactly(QPerson.person.firstname.desc());\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid toOrderSpecifierConvertsSortCorrectlyAndRetainsArgumentOrder() {\n\n\t\tSort sort = Sort.by(Direction.DESC, \"firstname\").and(Sort.by(Direction.ASC, \"age\"));\n\n\t\tOrderSpecifier<?>[] specifiers = toOrderSpecifier(sort, builder);\n\n\t\tassertThat(specifiers).containsExactly(QPerson.person.firstname.desc(), QPerson.person.age.asc());\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid toOrderSpecifierConvertsSortWithNullHandlingCorrectly() {\n\n\t\tSort sort = Sort.by(new Sort.Order(Direction.DESC, \"firstname\", NullHandling.NULLS_LAST));\n\n\t\tOrderSpecifier<?>[] specifiers = toOrderSpecifier(sort, builder);\n\n\t\tassertThat(specifiers).containsExactly(QPerson.person.firstname.desc().nullsLast());\n\t}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/keyvalue/repository/support/KeyValueRepositoryFactoryBeanUnitTests.java",
    "content": "/*\n * Copyright 2016-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.keyvalue.repository.support;\n\nimport static org.assertj.core.api.Assertions.*;\nimport static org.mockito.Mockito.*;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.data.keyvalue.core.KeyValueOperations;\nimport org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery;\nimport org.springframework.data.repository.Repository;\nimport org.springframework.data.repository.query.RepositoryQuery;\nimport org.springframework.data.repository.query.parser.AbstractQueryCreator;\n\n/**\n * Unit tests for {@link KeyValueRepositoryFactoryBean}.\n *\n * @author Oliver Gierke\n * @author Mark Paluch\n */\nclass KeyValueRepositoryFactoryBeanUnitTests {\n\n\tprivate KeyValueRepositoryFactoryBean<?, ?, ?> factoryBean;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tthis.factoryBean = new KeyValueRepositoryFactoryBean<Repository<Object, Object>, Object, Object>(\n\t\t\t\tSampleRepository.class);\n\t}\n\n\t@Test // DATAKV-123\n\tvoid rejectsNullKeyValueOperations() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> factoryBean.setKeyValueOperations(null));\n\t}\n\n\t@Test // DATAKV-123\n\tvoid rejectsNullQueryCreator() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> factoryBean.setQueryCreator(null));\n\t}\n\n\t@Test // DATAKV-123\n\tvoid rejectsUninitializedInstance() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> factoryBean.afterPropertiesSet());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test // DATAKV-123\n\tvoid rejectsInstanceWithoutKeyValueOperations() {\n\n\t\tClass<? extends AbstractQueryCreator<?, ?>> creatorType = (Class<? extends AbstractQueryCreator<?, ?>>) mock(\n\t\t\t\tAbstractQueryCreator.class).getClass();\n\n\t\tfactoryBean.setQueryCreator(creatorType);\n\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> factoryBean.afterPropertiesSet());\n\t}\n\n\t@Test // DATAKV-123\n\tvoid rejectsInstanceWithoutQueryCreator() {\n\n\t\tfactoryBean.setKeyValueOperations(mock(KeyValueOperations.class));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> factoryBean.afterPropertiesSet());\n\t}\n\n\t@Test // DATAKV-123\n\t@SuppressWarnings(\"unchecked\")\n\tvoid createsRepositoryFactory() {\n\n\t\tClass<? extends AbstractQueryCreator<?, ?>> creatorType = (Class<? extends AbstractQueryCreator<?, ?>>) mock(\n\t\t\t\tAbstractQueryCreator.class).getClass();\n\t\tClass<? extends RepositoryQuery> queryType = mock(KeyValuePartTreeQuery.class).getClass();\n\n\t\tfactoryBean.setQueryCreator(creatorType);\n\t\tfactoryBean.setKeyValueOperations(mock(KeyValueOperations.class));\n\t\tfactoryBean.setQueryType(queryType);\n\n\t\tassertThat(factoryBean.createRepositoryFactory()).isNotNull();\n\t}\n\n\t@Test // DATAKV-112\n\tvoid rejectsNullQueryType() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> factoryBean.setQueryType(null));\n\t}\n\n\tinterface SampleRepository extends Repository<Object, Object> {}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/map/AbstractRepositoryUnitTests.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.map;\n\nimport static org.assertj.core.api.Assertions.*;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.data.domain.Page;\nimport org.springframework.data.domain.PageRequest;\nimport org.springframework.data.domain.Pageable;\nimport org.springframework.data.domain.Sort;\nimport org.springframework.data.domain.Sort.Direction;\nimport org.springframework.data.keyvalue.Person;\nimport org.springframework.data.keyvalue.QPerson;\nimport org.springframework.data.keyvalue.core.KeyValueOperations;\nimport org.springframework.data.keyvalue.core.KeyValueTemplate;\nimport org.springframework.data.keyvalue.repository.KeyValueRepository;\nimport org.springframework.data.keyvalue.repository.support.KeyValueRepositoryFactory;\nimport org.springframework.data.repository.CrudRepository;\n\n/**\n * Base class for test cases for repository implementations.\n *\n * @author Christoph Strobl\n * @author Oliver Gierke\n * @author Thomas Darimont\n * @author Mark Paluch\n * @author Jens Schauder\n */\npublic abstract class AbstractRepositoryUnitTests<T extends AbstractRepositoryUnitTests.PersonRepository> {\n\n\tstatic final Person CERSEI = new Person(\"cersei\", 19);\n\tstatic final Person JAIME = new Person(\"jaime\", 19);\n\tstatic final Person TYRION = new Person(\"tyrion\", 17);\n\n\tstatic List<Person> LENNISTERS = Arrays.asList(CERSEI, JAIME, TYRION);\n\n\tprotected final QPerson person = QPerson.person;\n\n\tprotected T repository;\n\n\t@BeforeEach\n\tvoid setup() {\n\n\t\tKeyValueOperations operations = new KeyValueTemplate(new MapKeyValueAdapter());\n\t\tKeyValueRepositoryFactory keyValueRepositoryFactory = createKeyValueRepositoryFactory(operations);\n\n\t\tthis.repository = getRepository(keyValueRepositoryFactory);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid findBy() {\n\n\t\trepository.saveAll(LENNISTERS);\n\n\t\tassertThat(repository.findByAge(19)).contains(CERSEI, JAIME);\n\t}\n\n\t@Test // DATAKV-137\n\tvoid findByFirstname() {\n\n\t\trepository.saveAll(LENNISTERS);\n\n\t\tassertThat(repository.findByFirstname(CERSEI.getFirstname())).contains(CERSEI);\n\t\tassertThat(repository.findByFirstname(JAIME.getFirstname())).contains(JAIME);\n\t}\n\n\t@Test // DATACMNS-525, DATAKV-137\n\tvoid combindedFindUsingAnd() {\n\n\t\trepository.saveAll(LENNISTERS);\n\n\t\tassertThat(repository.findByFirstnameAndAge(JAIME.getFirstname(), 19)).contains(JAIME);\n\t\tassertThat(repository.findByFirstnameAndAge(TYRION.getFirstname(), 17)).contains(TYRION);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid findPage() {\n\n\t\trepository.saveAll(LENNISTERS);\n\n\t\tPage<Person> page = repository.findByAge(19, PageRequest.of(0, 1));\n\t\tassertThat(page.hasNext()).isTrue();\n\t\tassertThat(page.getTotalElements()).isEqualTo(2L);\n\t\tassertThat(page.getContent()).hasSize(1);\n\n\t\tPage<Person> next = repository.findByAge(19, page.nextPageable());\n\t\tassertThat(next.hasNext()).isFalse();\n\t\tassertThat(next.getTotalElements()).isEqualTo(2L);\n\t\tassertThat(next.getContent()).hasSize(1);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid findByConnectingOr() {\n\n\t\trepository.saveAll(LENNISTERS);\n\n\t\tassertThat(repository.findByAgeOrFirstname(19, TYRION.getFirstname())).contains(CERSEI, JAIME, TYRION);\n\t}\n\n\t@Test // DATACMNS-525, DATAKV-137\n\tvoid singleEntityExecution() {\n\n\t\trepository.saveAll(LENNISTERS);\n\n\t\tassertThat(repository.findByAgeAndFirstname(TYRION.getAge(), TYRION.getFirstname())).isEqualTo(TYRION);\n\t\tassertThat(repository.findByAgeAndFirstname(CERSEI.getAge(), CERSEI.getFirstname())).isEqualTo(CERSEI);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid findAllShouldRespectSort() {\n\n\t\trepository.saveAll(LENNISTERS);\n\n\t\tassertThat(\n\t\t\t\trepository.findAll(Sort.by(new Sort.Order(Direction.ASC, \"age\"), new Sort.Order(Direction.DESC, \"firstname\"))))\n\t\t\t\t\t\t.containsExactly(TYRION, JAIME, CERSEI);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid derivedFinderShouldRespectSort() {\n\n\t\trepository.saveAll(LENNISTERS);\n\n\t\tList<Person> result = repository.findByAgeGreaterThanOrderByAgeAscFirstnameDesc(2);\n\n\t\tassertThat(result).containsExactly(TYRION, JAIME, CERSEI);\n\t}\n\n\t@Test // DATAKV-121\n\tvoid projectsResultToInterface() {\n\n\t\trepository.saveAll(LENNISTERS);\n\n\t\tList<PersonSummary> result = repository.findByAgeGreaterThan(0, Sort.by(\"firstname\"));\n\n\t\tassertThat(result).hasSize(3);\n\t\tassertThat(result.get(0).getFirstname()).isEqualTo(CERSEI.getFirstname());\n\t}\n\n\t@Test // DATAKV-121\n\tvoid projectsResultToDynamicInterface() {\n\n\t\trepository.saveAll(LENNISTERS);\n\n\t\tList<PersonSummary> result = repository.findByAgeGreaterThan(0, Sort.by(\"firstname\"), PersonSummary.class);\n\n\t\tassertThat(result).hasSize(3);\n\t\tassertThat(result.get(0).getFirstname()).isEqualTo(CERSEI.getFirstname());\n\t}\n\n\t@Test // DATAKV-169\n\tvoid findsByValueInCollectionCorrectly() {\n\n\t\trepository.saveAll(LENNISTERS);\n\n\t\tList<Person> result = repository.findByFirstnameIn(Arrays.asList(CERSEI.getFirstname(), JAIME.getFirstname()));\n\n\t\tassertThat(result).hasSize(2);\n\t\tassertThat(result).contains(CERSEI, JAIME);\n\t}\n\n\t@Test // DATAKV-169\n\tvoid findsByValueInCollectionCorrectlyWhenTargetPathContainsNullValue() {\n\n\t\trepository.saveAll(LENNISTERS);\n\t\trepository.save(new Person(null, 10));\n\n\t\tList<Person> result = repository.findByFirstnameIn(Arrays.asList(CERSEI.getFirstname(), JAIME.getFirstname()));\n\n\t\tassertThat(result).hasSize(2);\n\t\tassertThat(result).contains(CERSEI, JAIME);\n\t}\n\n\t@Test // DATAKV-169\n\tvoid findsByValueInCollectionCorrectlyWhenTargetPathAndCollectionContainNullValue() {\n\n\t\trepository.saveAll(LENNISTERS);\n\n\t\tPerson personWithNullAsFirstname = new Person(null, 10);\n\t\trepository.save(personWithNullAsFirstname);\n\n\t\tList<Person> result = repository\n\t\t\t\t.findByFirstnameIn(Arrays.asList(CERSEI.getFirstname(), JAIME.getFirstname(), null));\n\n\t\tassertThat(result).hasSize(3);\n\t\tassertThat(result).contains(CERSEI, JAIME, personWithNullAsFirstname);\n\t}\n\n\tprotected KeyValueRepositoryFactory createKeyValueRepositoryFactory(KeyValueOperations operations) {\n\t\treturn new KeyValueRepositoryFactory(operations);\n\t}\n\n\tprotected abstract T getRepository(KeyValueRepositoryFactory factory);\n\n\tpublic interface PersonRepository extends CrudRepository<Person, String>, KeyValueRepository<Person, String> {\n\n\t\tList<Person> findByAge(int age);\n\n\t\tList<Person> findByFirstname(String firstname);\n\n\t\tList<Person> findByFirstnameAndAge(String firstname, int age);\n\n\t\tPage<Person> findByAge(int age, Pageable page);\n\n\t\tList<Person> findByAgeOrFirstname(int age, String firstname);\n\n\t\tPerson findByAgeAndFirstname(int age, String firstname);\n\n\t\tList<Person> findByAgeGreaterThanOrderByAgeAscFirstnameDesc(int age);\n\n\t\tList<PersonSummary> findByAgeGreaterThan(int age, Sort sort);\n\n\t\t<T> List<T> findByAgeGreaterThan(int age, Sort sort, Class<T> projectionType);\n\n\t\tList<Person> findByFirstnameIn(List<String> firstname);\n\t}\n\n\tinterface PersonSummary {\n\n\t\tString getFirstname();\n\t}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/map/CachingQuerySimpleKeyValueRepositoryUnitTests.java",
    "content": "/*\n * Copyright 2016-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.map;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.springframework.data.keyvalue.core.KeyValueOperations;\nimport org.springframework.data.keyvalue.repository.query.CachingKeyValuePartTreeQuery;\nimport org.springframework.data.keyvalue.repository.query.PredicateQueryCreator;\nimport org.springframework.data.keyvalue.repository.query.SpelQueryCreator;\nimport org.springframework.data.keyvalue.repository.support.KeyValueRepositoryFactory;\nimport org.springframework.data.keyvalue.repository.support.SimpleKeyValueRepository;\n\n/**\n * Unit tests for {@link SimpleKeyValueRepository} using {@link CachingKeyValuePartTreeQuery} and\n * {@link PredicateQueryCreator}.\n *\n * @author Mark Paluch\n * @author Christoph Strobl\n */\n@Disabled\npublic class CachingQuerySimpleKeyValueRepositoryUnitTests extends SimpleKeyValueRepositoryUnitTests {\n\n\t@Override\n\tprotected KeyValueRepositoryFactory createKeyValueRepositoryFactory(KeyValueOperations operations) {\n\t\treturn new KeyValueRepositoryFactory(operations, PredicateQueryCreator.class, CachingKeyValuePartTreeQuery.class);\n\t}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/map/MapDbIntegrationTests.java",
    "content": "/*\n * Copyright 2025-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.map;\n\nimport static org.assertj.core.api.Assertions.*;\n\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\nimport org.mapdb.DB;\nimport org.mapdb.DBMaker;\nimport org.mapdb.HTreeMap;\nimport org.mapdb.Serializer;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.keyvalue.repository.KeyValueRepository;\nimport org.springframework.data.map.repository.config.EnableMapRepositories;\nimport org.springframework.test.context.junit.jupiter.SpringJUnitConfig;\n\n/**\n * Example for MapDB integration testing the repository support through {@link KeySpaceStore}.\n *\n * @author Mark Paluch\n */\n@SpringJUnitConfig\nclass MapDbIntegrationTests {\n\n\t@Configuration\n\t@EnableMapRepositories(considerNestedRepositories = true, keySpaceStoreRef = \"store\")\n\tstatic class TestConfiguration {\n\n\t\t@Bean\n\t\tDB db() {\n\t\t\treturn DBMaker.heapDB().make();\n\t\t}\n\n\t\t@Bean\n\t\tKeySpaceStore store(DB db) {\n\n\t\t\treturn new KeySpaceStore() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic Map<Object, Object> getKeySpace(String keyspace) {\n\t\t\t\t\treturn db.hashMap(keyspace, Serializer.JAVA, Serializer.JAVA).createOrOpen();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic void clear() {\n\t\t\t\t\tdb.getStore().getAllRecids().forEachRemaining(it -> db.getStore().delete(it, Serializer.JAVA));\n\t\t\t\t}\n\n\t\t\t};\n\t\t}\n\n\t}\n\n\t@Autowired PersonRepository personRepository;\n\t@Autowired DB db;\n\n\t@Test\n\tvoid shouldStoreEntriesInMapDb() {\n\n\t\tPerson walter = personRepository.save(new Person(\"Walter\", \"White\"));\n\t\tpersonRepository.save(new Person(\"Skyler\", \"White\"));\n\t\tpersonRepository.save(new Person(\"Flynn\", \"White\"));\n\n\t\tassertThat(personRepository.countByLastname(\"White\")).isEqualTo(3);\n\n\t\tHTreeMap<String, Person> backingMap = db.hashMap(Person.class.getName(), Serializer.JAVA, Serializer.JAVA)\n\t\t\t\t.createOrOpen();\n\n\t\tassertThat(backingMap.size()).isEqualTo(3);\n\t\tassertThat(backingMap).containsEntry(walter.id, walter);\n\t}\n\n\tinterface PersonRepository extends KeyValueRepository<Person, String> {\n\n\t\tlong countByLastname(String lastname);\n\t}\n\n\tstatic class Person {\n\n\t\t@Id String id;\n\t\tString firstname;\n\t\tString lastname;\n\n\t\tPerson(String firstname, String lastname) {\n\t\t\tthis.firstname = firstname;\n\t\t\tthis.lastname = lastname;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/map/MapKeyValueAdapterUnitTests.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.map;\n\nimport static org.assertj.core.api.Assertions.*;\n\nimport java.util.AbstractMap;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.data.util.CloseableIterator;\n\n/**\n * Unit tests for {@link MapKeyValueAdapter}.\n *\n * @author Christoph Strobl\n * @author Mark Paluch\n */\nclass MapKeyValueAdapterUnitTests {\n\n\tprivate static final String COLLECTION_1 = \"collection-1\";\n\tprivate static final String COLLECTION_2 = \"collection-2\";\n\tprivate static final String STRING_1 = new String(\"1\");\n\n\tprivate Object object1 = new SimpleObject(\"one\");\n\tprivate Object object2 = new SimpleObject(\"two\");\n\n\tprivate MapKeyValueAdapter adapter;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tthis.adapter = new MapKeyValueAdapter(LinkedHashMap.class);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid putShouldThrowExceptionWhenAddingNullId() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> adapter.put(null, object1, COLLECTION_1));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid putShouldThrowExceptionWhenCollectionIsNullValue() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> adapter.put(\"1\", object1, null));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid putReturnsNullWhenNoObjectForIdPresent() {\n\t\tassertThat(adapter.put(\"1\", object1, COLLECTION_1)).isNull();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid putShouldReturnPreviousObjectForIdWhenAddingNewOneWithSameIdPresent() {\n\n\t\tadapter.put(\"1\", object1, COLLECTION_1);\n\t\tassertThat(adapter.put(\"1\", object2, COLLECTION_1)).isEqualTo(object1);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid containsShouldThrowExceptionWhenIdIsNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> adapter.contains(null, COLLECTION_1));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid containsShouldThrowExceptionWhenTypeIsNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> adapter.contains(\"\", null));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid containsShouldReturnFalseWhenNoElementsPresent() {\n\t\tassertThat(adapter.contains(\"1\", COLLECTION_1)).isFalse();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid containShouldReturnTrueWhenElementWithIdPresent() {\n\n\t\tadapter.put(\"1\", object1, COLLECTION_1);\n\t\tassertThat(adapter.contains(\"1\", COLLECTION_1)).isTrue();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid getShouldReturnNullWhenNoElementWithIdPresent() {\n\t\tassertThat(adapter.get(\"1\", COLLECTION_1)).isNull();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid getShouldReturnElementWhenMatchingIdPresent() {\n\n\t\tadapter.put(\"1\", object1, COLLECTION_1);\n\t\tassertThat(adapter.get(\"1\", COLLECTION_1)).isEqualTo(object1);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid getShouldThrowExceptionWhenIdIsNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> adapter.get(null, COLLECTION_1));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid getShouldThrowExceptionWhenTypeIsNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> adapter.get(\"1\", null));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid getAllOfShouldReturnAllValuesOfGivenCollection() {\n\n\t\tadapter.put(\"1\", object1, COLLECTION_1);\n\t\tadapter.put(\"2\", object2, COLLECTION_1);\n\t\tadapter.put(\"3\", STRING_1, COLLECTION_2);\n\n\t\tassertThat((Iterable) adapter.getAllOf(COLLECTION_1)).contains(object1, object2);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid getAllOfShouldThrowExceptionWhenTypeIsNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> adapter.getAllOf(null));\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid deleteShouldReturnNullWhenGivenIdThatDoesNotExist() {\n\t\tassertThat(adapter.delete(\"1\", COLLECTION_1)).isNull();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid deleteShouldReturnDeletedObject() {\n\n\t\tadapter.put(\"1\", object1, COLLECTION_1);\n\t\tassertThat(adapter.delete(\"1\", COLLECTION_1)).isEqualTo(object1);\n\t}\n\n\t@Test // DATAKV-99\n\tvoid scanShouldIterateOverAvailableEntries() {\n\n\t\tadapter.put(\"1\", object1, COLLECTION_1);\n\t\tadapter.put(\"2\", object2, COLLECTION_1);\n\n\t\tCloseableIterator<Map.Entry<Object, Object>> iterator = adapter.entries(COLLECTION_1);\n\n\t\tassertThat(iterator.next()).isEqualTo(new AbstractMap.SimpleEntry<>(\"1\", object1));\n\t\tassertThat(iterator.next()).isEqualTo(new AbstractMap.SimpleEntry<>(\"2\", object2));\n\t\tassertThat(iterator.hasNext()).isFalse();\n\t}\n\n\t@Test // DATAKV-99\n\tvoid scanShouldReturnEmptyIteratorWhenNoElementsAvailable() {\n\t\tassertThat(adapter.entries(COLLECTION_1).hasNext()).isFalse();\n\t}\n\n\t@Test // DATAKV-99\n\tvoid scanDoesNotMixResultsFromMultipleKeyspaces() {\n\n\t\tadapter.put(\"1\", object1, COLLECTION_1);\n\t\tadapter.put(\"2\", object2, COLLECTION_2);\n\n\t\tCloseableIterator<Map.Entry<Object, Object>> iterator = adapter.entries(COLLECTION_1);\n\n\t\tassertThat(iterator.next()).isEqualTo(new AbstractMap.SimpleEntry<>(\"1\", object1));\n\t\tassertThat(iterator.hasNext()).isFalse();\n\t}\n\n\tstatic class SimpleObject {\n\n\t\tprotected String stringValue;\n\n\t\tpublic SimpleObject() {}\n\n\t\tSimpleObject(String value) {\n\t\t\tthis.stringValue = value;\n\t\t}\n\n\t\tpublic String getStringValue() {\n\t\t\treturn this.stringValue;\n\t\t}\n\n\t\tpublic void setStringValue(String stringValue) {\n\t\t\tthis.stringValue = stringValue;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/map/QuerydslKeyValuePredicateExecutorUnitTests.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.map;\n\nimport static org.assertj.core.api.Assertions.*;\n\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Function;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.dao.IncorrectResultSizeDataAccessException;\nimport org.springframework.data.domain.Page;\nimport org.springframework.data.domain.PageRequest;\nimport org.springframework.data.domain.Sort;\nimport org.springframework.data.domain.Sort.Direction;\nimport org.springframework.data.keyvalue.Person;\nimport org.springframework.data.keyvalue.QPerson;\nimport org.springframework.data.keyvalue.repository.support.KeyValueRepositoryFactory;\nimport org.springframework.data.map.QuerydslKeyValuePredicateExecutorUnitTests.QPersonRepository;\nimport org.springframework.data.querydsl.QSort;\nimport org.springframework.data.querydsl.QuerydslPredicateExecutor;\nimport org.springframework.data.repository.query.FluentQuery;\nimport org.springframework.data.util.Streamable;\n\n/**\n * Unit tests for {@link org.springframework.data.keyvalue.repository.support.QuerydslKeyValuePredicateExecutor}.\n *\n * @author Christoph Strobl\n * @author Oliver Gierke\n * @author Thomas Darimont\n * @author Mark Paluch\n */\nclass QuerydslKeyValuePredicateExecutorUnitTests extends AbstractRepositoryUnitTests<QPersonRepository> {\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\trepository.saveAll(LENNISTERS);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid findOneIsExecutedCorrectly() {\n\n\t\tOptional<Person> result = repository.findOne(QPerson.person.firstname.eq(CERSEI.getFirstname()));\n\t\tassertThat(result).hasValue(CERSEI);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid findAllIsExecutedCorrectly() {\n\n\t\tIterable<Person> result = repository.findAll(QPerson.person.age.eq(CERSEI.getAge()));\n\t\tassertThat(result).contains(CERSEI, JAIME);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid findWithPaginationWorksCorrectly() {\n\n\t\tPage<Person> page1 = repository.findAll(QPerson.person.age.eq(CERSEI.getAge()), PageRequest.of(0, 1));\n\n\t\tassertThat(page1.getTotalElements()).isEqualTo(2L);\n\t\tassertThat(page1.getContent()).hasSize(1);\n\t\tassertThat(page1.hasNext()).isTrue();\n\n\t\tPage<Person> page2 = repository.findAll(QPerson.person.age.eq(CERSEI.getAge()), page1.nextPageable());\n\n\t\tassertThat(page2.getTotalElements()).isEqualTo(2L);\n\t\tassertThat(page2.getContent()).hasSize(1);\n\t\tassertThat(page2.hasNext()).isFalse();\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid findAllUsingOrderSpecifierWorksCorrectly() {\n\n\t\tIterable<Person> result = repository.findAll(QPerson.person.age.eq(CERSEI.getAge()),\n\t\t\t\tQPerson.person.firstname.desc());\n\n\t\tassertThat(result).containsExactly(JAIME, CERSEI);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid findAllUsingPageableWithSortWorksCorrectly() {\n\n\t\tIterable<Person> result = repository.findAll(QPerson.person.age.eq(CERSEI.getAge()),\n\t\t\t\tPageRequest.of(0, 10, Direction.DESC, \"firstname\"));\n\n\t\tassertThat(result).containsExactly(JAIME, CERSEI);\n\t}\n\n\t@Test // DATACMNS-525\n\tvoid findAllUsingPagableWithQSortWorksCorrectly() {\n\n\t\tIterable<Person> result = repository.findAll(QPerson.person.age.eq(CERSEI.getAge()),\n\t\t\t\tPageRequest.of(0, 10, new QSort(QPerson.person.firstname.desc())));\n\n\t\tassertThat(result).containsExactly(JAIME, CERSEI);\n\t}\n\n\t@Test // DATAKV-90\n\tvoid findAllWithOrderSpecifierWorksCorrectly() {\n\n\t\tIterable<Person> result = repository.findAll(new QSort(QPerson.person.firstname.desc()));\n\n\t\tassertThat(result).containsExactly(TYRION, JAIME, CERSEI);\n\t}\n\n\t@Test // DATAKV-90, DATAKV-197\n\tvoid findAllShouldRequireSort() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> repository.findAll((QSort) null));\n\t}\n\n\t@Test // DATAKV-90, DATAKV-197\n\tvoid findAllShouldAllowUnsortedFindAll() {\n\n\t\tIterable<Person> result = repository.findAll(Sort.unsorted());\n\n\t\tassertThat(result).contains(TYRION, JAIME, CERSEI);\n\t}\n\n\t@Test // DATAKV-95\n\tvoid executesExistsCorrectly() {\n\t\tassertThat(repository.exists(QPerson.person.age.eq(CERSEI.getAge()))).isTrue();\n\t}\n\n\t@Test // DATAKV-96\n\tvoid shouldSupportFindAllWithPredicateAndSort() {\n\n\t\tList<Person> users = Streamable.of(repository.findAll(person.age.gt(0), Sort.by(Direction.ASC, \"firstname\")))\n\t\t\t\t.toList();\n\n\t\tassertThat(users).hasSize(3);\n\t\tassertThat(users.get(0).getFirstname()).isEqualTo(CERSEI.getFirstname());\n\t\tassertThat(users.get(2).getFirstname()).isEqualTo(TYRION.getFirstname());\n\t\tassertThat(users).contains(CERSEI, JAIME, TYRION);\n\t}\n\n\t@Test // DATAKV-179\n\tvoid throwsExceptionIfMoreThanOneResultIsFound() {\n\n\t\tassertThatExceptionOfType(IncorrectResultSizeDataAccessException.class) //\n\t\t\t\t.isThrownBy(() -> repository.findOne(person.firstname.contains(\"e\")));\n\t}\n\n\t@Test // GH-397\n\tvoid findByShouldReturnFirst() {\n\n\t\tPerson first = repository.findBy(QPerson.person.firstname.eq(\"tyrion\"),\n\t\t\t\tFluentQuery.FetchableFluentQuery::firstValue);\n\n\t\tassertThat(first).isEqualTo(TYRION);\n\n\t\tfirst = repository.findBy(QPerson.person.firstname.eq(\"foo\"), Function.identity()).firstValue();\n\n\t\tassertThat(first).isNull();\n\t}\n\n\t@Test // GH-397\n\tvoid findByShouldReturnOne() {\n\n\t\tassertThatExceptionOfType(IncorrectResultSizeDataAccessException.class)\n\t\t\t\t.isThrownBy(() -> repository.findBy(QPerson.person.firstname.ne(\"foo\"), FluentQuery.FetchableFluentQuery::one));\n\n\t\tPerson one = repository.findBy(QPerson.person.firstname.eq(\"tyrion\"), FluentQuery.FetchableFluentQuery::oneValue);\n\n\t\tassertThat(one).isEqualTo(TYRION);\n\t}\n\n\t@Test // GH-397\n\tvoid findByShouldReturnFirstWithProjection() {\n\n\t\tPersonProjection interfaceProjection = repository.findBy(QPerson.person.firstname.eq(\"tyrion\"),\n\t\t\t\tit -> it.as(PersonProjection.class).firstValue());\n\t\tassertThat(interfaceProjection.getFirstname()).isEqualTo(\"tyrion\");\n\n\t\tPersonDto dto = repository.findBy(QPerson.person.firstname.eq(\"tyrion\"), it -> it.as(PersonDto.class).firstValue());\n\t\tassertThat(dto.getFirstname()).isEqualTo(\"tyrion\");\n\t}\n\n\t@Test // GH-397\n\tvoid findByShouldReturnOneWithProjection() {\n\n\t\tPersonProjection interfaceProjection = repository.findBy(QPerson.person.firstname.eq(\"tyrion\"),\n\t\t\t\tit -> it.as(PersonProjection.class).oneValue());\n\t\tassertThat(interfaceProjection.getFirstname()).isEqualTo(\"tyrion\");\n\n\t\tPersonDto dto = repository.findBy(QPerson.person.firstname.eq(\"tyrion\"), it -> it.as(PersonDto.class).oneValue());\n\t\tassertThat(dto.getFirstname()).isEqualTo(\"tyrion\");\n\t}\n\n\t@Test // GH-397\n\tvoid findByShouldReturnAll() {\n\n\t\tList<Person> all = repository.findBy(QPerson.person.firstname.eq(\"tyrion\"), FluentQuery.FetchableFluentQuery::all);\n\n\t\tassertThat(all).contains(TYRION);\n\t}\n\n\t@Test // GH-397\n\tvoid findByShouldReturnAllSorted() {\n\n\t\tList<Person> all = repository.findBy(QPerson.person.firstname.ne(\"foo\"),\n\t\t\t\tq -> q.sortBy(Sort.by(Direction.ASC, \"firstname\")).all());\n\n\t\tassertThat(all).containsSequence(CERSEI, JAIME, TYRION);\n\n\t\tall = repository.findBy(QPerson.person.firstname.ne(\"foo\"),\n\t\t\t\tq -> q.sortBy(Sort.by(Direction.DESC, \"firstname\")).all());\n\n\t\tassertThat(all).containsSequence(TYRION, JAIME, CERSEI);\n\t}\n\n\t@Test // GH-397\n\tvoid findByShouldReturnAllWithProjection() {\n\n\t\tStream<PersonProjection> all = repository.findBy(QPerson.person.firstname.eq(\"tyrion\"),\n\t\t\t\tq -> q.as(PersonProjection.class).stream());\n\n\t\tassertThat(all).hasOnlyElementsOfType(PersonProjection.class);\n\t}\n\n\t@Test // GH-397\n\tvoid findByShouldReturnPage() {\n\n\t\tPage<PersonProjection> page = repository.findBy(QPerson.person.firstname.ne(\"foo\"),\n\t\t\t\tit -> it.as(PersonProjection.class).page(PageRequest.of(0, 1, Sort.by(\"firstname\"))));\n\n\t\tassertThat(page.getContent().get(0).getFirstname()).isEqualTo(\"cersei\");\n\t\tassertThat(page.getTotalPages()).isEqualTo(3);\n\n\t\tPage<PersonProjection> nextPage = repository.findBy(QPerson.person.firstname.ne(\"foo\"),\n\t\t\t\tit -> it.as(PersonProjection.class).page(page.nextPageable()));\n\n\t\tassertThat(nextPage.getContent().get(0).getFirstname()).isEqualTo(\"jaime\");\n\t\tassertThat(nextPage.getTotalPages()).isEqualTo(3);\n\t}\n\n\t@Test // GH-397\n\tvoid findByShouldReturnStream() {\n\n\t\tList<Person> all = repository.findBy(QPerson.person.firstname.eq(\"tyrion\"), FluentQuery.FetchableFluentQuery::all);\n\n\t\tassertThat(all).contains(TYRION);\n\t}\n\n\t@Test // GH-397\n\tvoid findByShouldReturnStreamWithProjection() {\n\n\t\tStream<PersonProjection> all = repository.findBy(QPerson.person.firstname.eq(\"tyrion\"),\n\t\t\t\tq -> q.as(PersonProjection.class).stream());\n\n\t\tassertThat(all).hasOnlyElementsOfType(PersonProjection.class);\n\t}\n\n\t@Test // GH-397\n\tvoid findByShouldReturnCount() {\n\n\t\tlong count = repository.findBy(QPerson.person.firstname.ne(\"foo\"), FluentQuery.FetchableFluentQuery::count);\n\n\t\tassertThat(count).isEqualTo(3);\n\t}\n\n\t@Test // GH-397\n\tvoid findByShouldReturnExists() {\n\n\t\tboolean exists = repository.findBy(QPerson.person.firstname.eq(\"tyrion\"), FluentQuery.FetchableFluentQuery::exists);\n\t\tassertThat(exists).isTrue();\n\n\t\texists = repository.findBy(QPerson.person.firstname.eq(\"foo\"), FluentQuery.FetchableFluentQuery::exists);\n\t\tassertThat(exists).isFalse();\n\t}\n\n\tinterface PersonProjection {\n\t\tString getFirstname();\n\t}\n\n\tstatic class PersonDto {\n\n\t\tString firstname;\n\n\t\tpublic String getFirstname() {\n\t\t\treturn this.firstname;\n\t\t}\n\n\t\tpublic void setFirstname(String firstname) {\n\t\t\tthis.firstname = firstname;\n\t\t}\n\t}\n\n\t@Override\n\tprotected QPersonRepository getRepository(KeyValueRepositoryFactory factory) {\n\t\treturn factory.getRepository(QPersonRepository.class);\n\t}\n\n\tinterface QPersonRepository extends org.springframework.data.map.AbstractRepositoryUnitTests.PersonRepository,\n\t\t\tQuerydslPredicateExecutor<Person> {}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/map/SimpleKeyValueRepositoryUnitTests.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.map;\n\nimport org.springframework.data.keyvalue.repository.support.KeyValueRepositoryFactory;\nimport org.springframework.data.keyvalue.repository.support.SimpleKeyValueRepository;\nimport org.springframework.data.map.AbstractRepositoryUnitTests.PersonRepository;\n\n/**\n * Unit tests for {@link SimpleKeyValueRepository}.\n *\n * @author Christoph Strobl\n * @author Oliver Gierke\n */\npublic class SimpleKeyValueRepositoryUnitTests extends AbstractRepositoryUnitTests<PersonRepository> {\n\n\tprotected PersonRepository getRepository(KeyValueRepositoryFactory factory) {\n\t\treturn factory.getRepository(PersonRepository.class);\n\t}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/map/repository/config/MapRepositoriesConfigurationExtensionIntegrationTests.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.map.repository.config;\n\nimport static org.assertj.core.api.Assertions.*;\nimport static org.mockito.Mockito.*;\n\nimport java.util.Arrays;\nimport java.util.Comparator;\nimport java.util.Iterator;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentSkipListMap;\nimport java.util.function.Predicate;\n\nimport org.assertj.core.api.InstanceOfAssertFactories;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.AnnotationConfigApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.FilterType;\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.domain.Sort;\nimport org.springframework.data.keyvalue.core.KeyValueAdapter;\nimport org.springframework.data.keyvalue.core.KeyValueOperations;\nimport org.springframework.data.keyvalue.core.KeyValueTemplate;\nimport org.springframework.data.keyvalue.core.PathSortAccessor;\nimport org.springframework.data.keyvalue.core.QueryEngine;\nimport org.springframework.data.keyvalue.core.QueryEngineFactory;\nimport org.springframework.data.keyvalue.core.SortAccessor;\nimport org.springframework.data.keyvalue.core.SpelQueryEngine;\nimport org.springframework.data.keyvalue.core.query.KeyValueQuery;\nimport org.springframework.data.keyvalue.repository.KeyValueRepository;\nimport org.springframework.data.keyvalue.repository.query.PredicateQueryCreator;\nimport org.springframework.data.keyvalue.repository.support.KeyValueRepositoryFactoryBean;\nimport org.springframework.data.map.KeySpaceStore;\nimport org.springframework.data.map.MapKeyValueAdapter;\nimport org.springframework.data.repository.query.ParameterAccessor;\nimport org.springframework.data.repository.query.parser.AbstractQueryCreator;\nimport org.springframework.data.repository.query.parser.Part;\nimport org.springframework.data.repository.query.parser.PartTree;\nimport org.springframework.test.util.ReflectionTestUtils;\n\n/**\n * Integration tests for {@link MapRepositoryConfigurationExtension}.\n *\n * @author Oliver Gierke\n * @author Christoph Strobl\n * @author Mark Paluch\n */\nclass MapRepositoriesConfigurationExtensionIntegrationTests {\n\n\t@Test // DATAKV-86\n\tvoid registersDefaultTemplateIfReferenceNotCustomized() {\n\n\t\tConfigurableApplicationContext context = new AnnotationConfigApplicationContext(Config.class);\n\n\t\tassertThat(Arrays.asList(context.getBeanDefinitionNames())).contains(\"mapKeyValueTemplate\");\n\n\t\tcontext.close();\n\t}\n\n\t@Test // DATAKV-86\n\tvoid doesNotRegisterDefaultTemplateIfReferenceIsCustomized() {\n\n\t\tConfigurableApplicationContext context = new AnnotationConfigApplicationContext(\n\t\t\t\tConfigWithCustomTemplateReference.class);\n\n\t\tassertThat(context.getBeanDefinitionNames()).doesNotContain(\"mapKeyValueTemplate\");\n\n\t\tcontext.close();\n\t}\n\n\t@Test // GH-358\n\tvoid shouldUseCustomAdapter() {\n\n\t\tConfigurableApplicationContext context = new AnnotationConfigApplicationContext(\n\t\t\t\tConfigWithOverriddenTemplateReference.class);\n\n\t\tPersonRepository repository = context.getBean(PersonRepository.class);\n\n\t\tassertThatThrownBy(() -> repository.findById(\"foo\")).hasRootCauseInstanceOf(IllegalStateException.class)\n\t\t\t\t.hasMessageContaining(\"Mock\");\n\n\t\tcontext.close();\n\t}\n\n\t@Test // DATAKV-87\n\tvoid considersMapTypeConfiguredOnAnnotation() {\n\t\tassertKeyValueTemplateWithAdapterFor(ConcurrentSkipListMap.class,\n\t\t\t\tnew AnnotationConfigApplicationContext(ConfigWithCustomizedMapType.class));\n\t}\n\n\t@Test // DATAKV-87\n\tvoid doesNotConsiderMapConfiguredIfTemplateIsPresent() {\n\t\tassertKeyValueTemplateWithAdapterFor(ConcurrentHashMap.class, new AnnotationConfigApplicationContext(\n\t\t\t\tConfigWithCustomizedMapTypeAndExplicitDefinitionOfKeyValueTemplate.class));\n\t}\n\n\t@Test // GH-565\n\tvoid considersSortAccessorConfiguredOnAnnotation() {\n\t\tassertKeyValueTemplateWithSortAccessorFor(PathSortAccessor.class,\n\t\t\t\tnew AnnotationConfigApplicationContext(ConfigWithCustomizedSortAccessor.class));\n\t}\n\n\t@Test // GH-576\n\tvoid considersQueryEngineConfiguration() {\n\n\t\tAnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigWithQueryEngine.class);\n\n\t\tKeyValueTemplate template = context.getBean(KeyValueTemplate.class);\n\t\tObject adapter = ReflectionTestUtils.getField(template, \"adapter\");\n\n\t\tassertThat(adapter).isInstanceOf(MapKeyValueAdapter.class);\n\n\t\tObject engine = ReflectionTestUtils.getField(adapter, \"engine\");\n\n\t\tassertThat(engine).isInstanceOf(SpelQueryEngine.class);\n\t}\n\n\t@Test // GH-576\n\tvoid considersQueryEngineAndSortAccessorConfiguration() {\n\n\t\tAnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(\n\t\t\t\tConfigWithQueryEngineAndCustomizedSortAccessor.class);\n\n\t\tKeyValueTemplate template = context.getBean(KeyValueTemplate.class);\n\t\tObject adapter = ReflectionTestUtils.getField(template, \"adapter\");\n\n\t\tassertThat(adapter).isInstanceOf(MapKeyValueAdapter.class);\n\n\t\tObject engine = ReflectionTestUtils.getField(adapter, \"engine\");\n\n\t\tassertThat(engine).isInstanceOf(SpelQueryEngine.class);\n\t\tObject sortAccessor = ReflectionTestUtils.getField(engine, \"sortAccessor\");\n\n\t\tassertThat(sortAccessor).asInstanceOf(InstanceOfAssertFactories.OPTIONAL)\n\t\t\t\t.containsInstanceOf(PathSortAccessor.class);\n\t}\n\n\tprivate static void assertKeyValueTemplateWithAdapterFor(Class<?> mapType, ApplicationContext context) {\n\n\t\tKeyValueTemplate template = context.getBean(KeyValueTemplate.class);\n\t\tObject adapter = ReflectionTestUtils.getField(template, \"adapter\");\n\n\t\tassertThat(adapter).isInstanceOf(MapKeyValueAdapter.class);\n\n\t\tKeySpaceStore store = (KeySpaceStore) ReflectionTestUtils.getField(adapter, \"store\");\n\t\tassertThat(ReflectionTestUtils.getField(store, \"store\")).isInstanceOf(mapType);\n\t}\n\n\tprivate static void assertKeyValueTemplateWithSortAccessorFor(Class<?> sortAccessorType, ApplicationContext context) {\n\n\t\tKeyValueTemplate template = context.getBean(KeyValueTemplate.class);\n\t\tObject adapter = ReflectionTestUtils.getField(template, \"adapter\");\n\n\t\tassertThat(adapter).isInstanceOf(MapKeyValueAdapter.class);\n\n\t\tObject engine = ReflectionTestUtils.getField(adapter, \"engine\");\n\t\tObject sortAccessor = ReflectionTestUtils.getField(engine, \"sortAccessor\");\n\n\t\tassertThat(sortAccessor).asInstanceOf(InstanceOfAssertFactories.OPTIONAL).containsInstanceOf(sortAccessorType);\n\t}\n\n\t@Test // GH-576\n\tvoid considersDefaultQueryCreator() {\n\n\t\tAnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);\n\n\t\tKeyValueRepositoryFactoryBean<?, ?, ?> factoryBean = context.getBean(KeyValueRepositoryFactoryBean.class);\n\t\tObject queryCreator = ReflectionTestUtils.getField(factoryBean, \"queryCreator\");\n\n\t\tassertThat(queryCreator).isEqualTo(PredicateQueryCreator.class);\n\t}\n\n\t@Test // GH-576\n\tvoid considersCustomQueryCreator() {\n\n\t\tAnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(\n\t\t\t\tConfigWithCustomQueryCreator.class);\n\n\t\tKeyValueRepositoryFactoryBean<?, ?, ?> factoryBean = context.getBean(KeyValueRepositoryFactoryBean.class);\n\t\tObject queryCreator = ReflectionTestUtils.getField(factoryBean, \"queryCreator\");\n\n\t\tassertThat(queryCreator).isEqualTo(MyQueryCreator.class);\n\t}\n\n\t@Configuration\n\t@EnableMapRepositories(considerNestedRepositories = true,\n\t\t\tincludeFilters = @ComponentScan.Filter(value = PersonRepository.class, type = FilterType.ASSIGNABLE_TYPE))\n\tstatic class Config {}\n\n\t@Configuration\n\t@EnableMapRepositories(keyValueTemplateRef = \"foo\")\n\tstatic class ConfigWithCustomTemplateReference {}\n\n\t@Configuration\n\t@EnableMapRepositories(considerNestedRepositories = true,\n\t\t\tincludeFilters = @ComponentScan.Filter(value = PersonRepository.class, type = FilterType.ASSIGNABLE_TYPE))\n\tstatic class ConfigWithOverriddenTemplateReference {\n\n\t\t@Bean\n\t\tpublic KeyValueOperations mapKeyValueTemplate() {\n\t\t\treturn new KeyValueTemplate(keyValueAdapter());\n\t\t}\n\n\t\t@Bean\n\t\tpublic KeyValueAdapter keyValueAdapter() {\n\n\t\t\tKeyValueAdapter mock = mock(KeyValueAdapter.class);\n\n\t\t\twhen(mock.get(any(), anyString(), any())).thenThrow(new IllegalStateException(\"Mock\"));\n\n\t\t\treturn mock;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableMapRepositories(mapType = ConcurrentSkipListMap.class)\n\tstatic class ConfigWithCustomizedMapType {}\n\n\t@Configuration\n\t@EnableMapRepositories(mapType = ConcurrentSkipListMap.class)\n\tstatic class ConfigWithCustomizedMapTypeAndExplicitDefinitionOfKeyValueTemplate {\n\n\t\t@Bean\n\t\tpublic KeyValueTemplate mapKeyValueTemplate() {\n\t\t\treturn new KeyValueTemplate(new MapKeyValueAdapter());\n\t\t}\n\t}\n\n\t@EnableMapRepositories(queryEngineFactory = JustSpelQueryEngineFactory.class)\n\tstatic class ConfigWithQueryEngine {}\n\n\tstatic class JustSpelQueryEngineFactory implements QueryEngineFactory {\n\n\t\t@Override\n\t\tpublic QueryEngine<?, ?, ?> create() {\n\t\t\treturn new SpelQueryEngine();\n\t\t}\n\n\t}\n\n\t@EnableMapRepositories(sortAccessor = PathSortAccessor.class)\n\tstatic class ConfigWithCustomizedSortAccessor {}\n\n\t@EnableMapRepositories(sortAccessor = PathSortAccessor.class, queryEngineFactory = SpelQueryEngineFactory.class)\n\tstatic class ConfigWithQueryEngineAndCustomizedSortAccessor {}\n\n\t@EnableMapRepositories(queryCreator = MyQueryCreator.class, considerNestedRepositories = true,\n\t\t\tincludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = PersonRepository.class))\n\tstatic class ConfigWithCustomQueryCreator {}\n\n\tstatic class SpelQueryEngineFactory implements QueryEngineFactory {\n\n\t\tprivate final SortAccessor<Comparator<?>> sortAccessor;\n\n\t\tpublic SpelQueryEngineFactory(SortAccessor<Comparator<?>> sortAccessor) {\n\t\t\tthis.sortAccessor = sortAccessor;\n\t\t}\n\n\t\t@Override\n\t\tpublic QueryEngine<?, ?, ?> create() {\n\t\t\treturn new SpelQueryEngine(sortAccessor);\n\t\t}\n\n\t}\n\n\tstatic class MyQueryCreator extends AbstractQueryCreator<KeyValueQuery<Predicate<?>>, Predicate<?>> {\n\n\t\tpublic MyQueryCreator(PartTree tree) {\n\t\t\tsuper(tree);\n\t\t}\n\n\t\tpublic MyQueryCreator(PartTree tree, ParameterAccessor parameters) {\n\t\t\tsuper(tree, parameters);\n\t\t}\n\n\t\t@Override\n\t\tprotected Predicate<?> create(Part part, Iterator<Object> iterator) {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tprotected Predicate<?> and(Part part, Predicate<?> base, Iterator<Object> iterator) {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tprotected Predicate<?> or(Predicate<?> base, Predicate<?> criteria) {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tprotected KeyValueQuery<Predicate<?>> complete(Predicate<?> criteria, Sort sort) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tinterface PersonRepository extends KeyValueRepository<Person, String> {\n\n\t}\n\n\tstatic class Person {\n\t\t@Id String id;\n\t\tString name;\n\n\t\tpublic String getId() {\n\t\t\treturn this.id;\n\t\t}\n\n\t\tpublic String getName() {\n\t\t\treturn this.name;\n\t\t}\n\n\t\tpublic void setId(String id) {\n\t\t\tthis.id = id;\n\t\t}\n\n\t\tpublic void setName(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/map/repository/config/MapRepositoryRegistrarWithFullDefaultingIntegrationTests.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.map.repository.config;\n\nimport static org.assertj.core.api.Assertions.*;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.repository.CrudRepository;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\n/**\n * Integration tests for {@link MapRepositoriesRegistrar} with complete defaulting.\n *\n * @author Christoph Strobl\n * @author Mark Paluch\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\npublic class MapRepositoryRegistrarWithFullDefaultingIntegrationTests {\n\n\t@Configuration\n\t@EnableMapRepositories(considerNestedRepositories = true)\n\tstatic class Config {\n\n\t}\n\n\t@Autowired PersonRepository repo;\n\n\t@Test // DATAKV-86\n\tvoid shouldEnableMapRepositoryCorrectly() {\n\t\tassertThat(repo).isNotNull();\n\t}\n\n\tstatic class Person {\n\n\t\t@Id String id;\n\t\tString firstname;\n\n\t\tpublic String getId() {\n\t\t\treturn this.id;\n\t\t}\n\n\t\tpublic String getFirstname() {\n\t\t\treturn this.firstname;\n\t\t}\n\n\t\tpublic void setId(String id) {\n\t\t\tthis.id = id;\n\t\t}\n\n\t\tpublic void setFirstname(String firstname) {\n\t\t\tthis.firstname = firstname;\n\t\t}\n\t}\n\n\tinterface PersonRepository extends CrudRepository<Person, String> {\n\n\t\tList<Person> findByFirstname(String firstname);\n\t}\n}\n"
  },
  {
    "path": "src/test/java/org/springframework/data/map/repository/config/MapRepositoryRegistrarWithTemplateDefinitionIntegrationTests.java",
    "content": "/*\n * Copyright 2014-present the original author or authors.\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 *      https://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.\n */\npackage org.springframework.data.map.repository.config;\n\nimport static org.assertj.core.api.Assertions.*;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.keyvalue.core.KeyValueOperations;\nimport org.springframework.data.keyvalue.core.KeyValueTemplate;\nimport org.springframework.data.map.MapKeyValueAdapter;\nimport org.springframework.data.repository.CrudRepository;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\n/**\n * Integration tests for {@link MapRepositoriesRegistrar} with complete defaulting.\n *\n * @author Christoph Strobl\n * @author Mark Paluch\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\npublic class MapRepositoryRegistrarWithTemplateDefinitionIntegrationTests {\n\n\t@Configuration\n\t@EnableMapRepositories(considerNestedRepositories = true)\n\tstatic class Config {\n\n\t\t@Bean\n\t\tpublic KeyValueOperations keyValueTemplate() {\n\t\t\treturn new KeyValueTemplate(new MapKeyValueAdapter());\n\t\t}\n\t}\n\n\t@Autowired PersonRepository repo;\n\n\t@Test // DATACMNS-525\n\tvoid shouldEnableMapRepositoryCorrectly() {\n\t\tassertThat(repo).isNotNull();\n\t}\n\n\tstatic class Person {\n\n\t\t@Id String id;\n\t\tString firstname;\n\n\t\tpublic String getId() {\n\t\t\treturn this.id;\n\t\t}\n\n\t\tpublic String getFirstname() {\n\t\t\treturn this.firstname;\n\t\t}\n\n\t\tpublic void setId(String id) {\n\t\t\tthis.id = id;\n\t\t}\n\n\t\tpublic void setFirstname(String firstname) {\n\t\t\tthis.firstname = firstname;\n\t\t}\n\t}\n\n\tinterface PersonRepository extends CrudRepository<Person, String> {\n\n\t\tList<Person> findByFirstname(String firstname);\n\t}\n}\n"
  },
  {
    "path": "src/test/resources/logback.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration>\n\n\t<appender name=\"console\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t\t<encoder>\n\t\t\t<pattern>%d %5p %40.40c:%4L - %m%n</pattern>\n\t\t</encoder>\n\t</appender>\n\n\t<logger name=\"org.springframework.data\" level=\"warn\" />\n\n\t<root level=\"warn\">\n\t\t<appender-ref ref=\"console\" />\n\t</root>\n\n</configuration>"
  }
]