diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b86d24f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# top-most EditorConfig file +root = true + +[*] +# Unix-style newlines with a newline ending every file +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +# Tab indentation size 4 +indent_style = tab +indent_size = 4 +# Set default charset +charset = utf-8 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2030490 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,74 @@ +# Text files with LF eol +*.auth crlf=input +*.awk crlf=input +*.bnd crlf=input +*.bndrun crlf=input +*.c crlf=input ident +*.conf crlf=input +*.cpp crlf=input ident +*.css crlf=input +*.ddf crlf=input +*.ee crlf=input +*.gradle crlf=input +*.groovy crlf=input +*.h crlf=input ident +*.html crlf=input ident +*.java crlf=input ident +*.js crlf=input +*.lib crlf=input +*.md crlf=input +*.MF crlf=input +*.mf crlf=input +*.perm crlf=input +*.php crlf=input +*.pl crlf=input +*.pom crlf=input +*.prefs crlf=input +*.properties crlf=input +*.py crlf=input +*.schema crlf=input +*.SF crlf=input +*.sh crlf=input +*.svg crlf=input ident +*.tcl crlf=input +*.txt crlf=input +*.xml crlf=input ident +*.xsd crlf=input ident +*.xsl crlf=input ident +*.xslt crlf=input ident +*.yml crlf=input +.classpath crlf=input +.project crlf=input +gradlew crlf=input +packageinfo crlf=input +Makefile crlf=input + +# No EOL translation +*.bat -crlf + +# Binary. No EOL translation, no diff +*.ico binary +*.jpeg binary +*.jpg binary +*.png binary +*.crt binary +*.pdf binary +*.dll binary +*.jar binary +*.jnilib binary +*.so binary +*.zip binary +*.doc binary +*.ppt binary +*.xls binary +*.odg binary +*.odp binary +*.ods binary +*.odt binary +*.otg binary +*.otp binary +*.ots binary +*.ott binary +*.key binary +*.numbers binary +*.pages binary diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..56f5f6c --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,21 @@ +# Managed by otterdog blueprint require-ci-workflows. Do not edit here. +# CI logic lives in osgi/.github (reusable workflow). +# Every push to main deploys a fresh SNAPSHOT to Sonatype Central +# snapshots; the weekly schedule keeps it fresh in quiet times. +name: build +on: + push: + branches: [ main ] + pull_request: + workflow_dispatch: + schedule: + - cron: '23 3 * * 1' +permissions: + contents: read + checks: write +jobs: + build: + uses: osgi/.github/.github/workflows/spec-build.yml@main + secrets: inherit + with: + deploy: ${{ github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..9f64855 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,13 @@ +# Managed by otterdog blueprint require-ci-workflows. Do not edit here. +# CI logic lives in osgi/.github (reusable workflow). +# A release is cut by pushing a tag that equals the pom version (no prefix); +# the central workflow refuses a tag that does not match .mvn/maven.config. +name: release +on: + push: + tags: + - '[0-9]*.[0-9]*.[0-9]*' +jobs: + release: + uses: osgi/.github/.github/workflows/spec-release.yml@main + secrets: inherit diff --git a/.github/workflows/scorecard-analysis.yml b/.github/workflows/scorecard-analysis.yml new file mode 100644 index 0000000..5f0bcf0 --- /dev/null +++ b/.github/workflows/scorecard-analysis.yml @@ -0,0 +1,30 @@ +# Managed by otterdog blueprint scorecard-integration. +name: Scorecard analysis +on: + branch_protection_rule: + schedule: + - cron: '43 5 * * 2' + push: + branches: [ main ] +permissions: read-all +jobs: + analysis: + runs-on: ubuntu-latest + permissions: + security-events: write + id-token: write + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Run analysis + uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4 + with: + results_file: results.sarif + results_format: sarif + publish_results: true + - name: Upload to code-scanning + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: results.sarif diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c16d64d --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +target/ +bin/ +bin_test/ +generated/ +.classpath +.project +.settings/ + +# flatten-maven-plugin resolves ${revision} into this file at build time +.flattened-pom.xml diff --git a/.mvn/maven.config b/.mvn/maven.config new file mode 100644 index 0000000..31252bf --- /dev/null +++ b/.mvn/maven.config @@ -0,0 +1 @@ +-Drevision=1.2.1-SNAPSHOT diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..1a6ea92 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,320 @@ +# Contributing to the OSGi Specification Project + +Here are instructions to get you started. +They are probably not perfect, please let us know if anything feels wrong or incomplete. + +## Project + +[https://github.com/osgi/osgi](https://github.com/osgi/osgi) is the main git repository for the [OSGi Specification Project](https://projects.eclipse.org/projects/technology.osgi). +Our primary written communication channel is the project's email list: [osgi-dev@eclipse.org](https://accounts.eclipse.org/mailing-list/osgi-dev). +We also have a [Slack workspace](https://osgiwg.slack.com) for more informal communications. +Contact a project committer for an invite to the Slack workspace. + +Issues can be reported in the [GitHub issue tracker](https://github.com/osgi/osgi/issues). + +## Build Environment + +The only thing you need to build OSGi is Java. +We require Java 8. +If your main JDK is higher than 8 you can set it on each run by adding `-Dorg.gradle.java.home=` to the Gradle command. + +We use Gradle to build and the repository includes `gradlew`. + +- `./gradlew :build :publish` - Builds and releases the artifacts into `cnf/generated/repo`. +- `./gradlew :osgi.specs:specifications` - Builds the specifications into `osgi.specs/generated`. + +We use [GitHub Actions](https://github.com/osgi/osgi/actions?query=workflow%3A%22CI%20Build%22) for continuous integration and the repo includes a `.github/workflows/cibuild.yml` file to build via GitHub Actions. + +### Building the Specifications + +The specifications can be built in different formats. +Currently available are `html` and `pdf`. +You can also build a zip file of the complete html format. + +The following tasks can be used: + +- `./gradlew :osgi.specs:core.pdf` +- `./gradlew :osgi.specs:core.html` +- `./gradlew :osgi.specs:core.zip` +- `./gradlew :osgi.specs:cmpn.pdf` +- `./gradlew :osgi.specs:cmpn.html` +- `./gradlew :osgi.specs:cmpn.zip` + +## IDE Setup + +Prerequisites are Eclipse ("Eclipse IDE for Java Developers") and [Bndtools](https://bndtools.org/). + +Before importing the project into Eclipse, it is recommended to do a full build as described above. +Then use `Import...` > `Existing Projects into Workspace` in Eclipse. + +### Run / Debug a TCK + +In Eclipse, the TCK tests for a project can be run using `Run As` > `Bnd OSGi Test Launcher (JUnit)` or debugged using `Debug As`/`Debug As` > `Bnd OSGi Test Launcher (JUnit)`. + +From the command line, you use the `testOSGi` Gradle task of the project. For example, run the TCK for Remote Service Admin using this command: + +```script +./gradlew :org.osgi.test.cases.remoteserviceadmin:testOSGi +``` + +When running the TCK from the TCK project folder, you are running the TCK in git working directory while the TCK must run, for compatibility testing purposes, outside of the development environment. +The `osgi.tck` project packages the TCKs into the form for which users will be expected to run the TCKs. +The CI build will execute the TCKs using the packaged form built by the `osgi.tck` project rather than running the `testOSGi` tasks. + +## Workflow + +We use [git triangular workflow](https://github.blog/2015-07-29-git-2-5-including-multiple-worktrees-and-triangular-workflows/). +This means you should not push contributions directly into the [main OSGi repository](https://github.com/osgi/osgi). +All contributions should come in through pull requests. +So each contributor will need to [fork the main OSGi repository](https://github.com/osgi/osgi/fork) on GitHub. +All contributions are made as commits to your fork. +Then you submit a pull request to have them considered for merging into the main OSGi repository. + +### Setting up the triangular workflow + +After forking the main OSGi repository on GitHub, you can clone the main repository to your system: + +```script +git clone https://github.com/osgi/osgi.git +``` + +This will clone the main repository to a local repository on your disk and set up the `origin` remote in Git. +Next you will set up the the second side of the triangle to your fork repository. + +```script +cd osgi +git remote add fork git@github.com:github-user/osgi.git +``` + +Make sure to replace the URL with the SSH URL to your fork repository on GitHub. +Then we configure the local repository to push your commits to the fork repository. + +```script +git config remote.pushdefault fork +``` + +So now you will pull from `origin`, the main repository, and push to `fork`, your fork repository. +This option requires at least Git 1.8.4 +It is also recommended that you configure + +```script +git config push.default simple +``` + +unless you are already using Git 2.0 where it is the default. + +Finally, the third side of the triangle is pull requests from your fork repository to the +main repository. + +## Contribution guidelines + +For the development of non-trivial new features, this project will first undertake a requirements discussion and, if the requirements discussion concludes successfully, then a design discussion. +By _new feature_, we mean a new specification or a non-trivial enhancement to an existing specification. + +Requirements discussions and design discussions should be used whenever there is a benefit to the project for the clarity gained by such discussions. +For a minor enhancement to an existing specification, we can sometimes skip the requirements discussion and include the requirements in the design discussion. + +If the design discussion concludes successfully, we can then move to integrate the new feature into the project which requires specification writing and API and TCK development as well as coordination with the development of a [compatible implementation](https://www.eclipse.org/projects/handbook/#specifications-implementations). +Each discussion will occur in its own branch of the git repository (see below for specific details). +This allows the discussion document along with any supporting code to be committed in the branch and for contributors to the discussion to make pull requests against the branch to suggest changes. + +The requirement discussion and design discussion documents are stored in the `.design` folder of the repository. +This is done so the folder is at the top of the GitHub repository web page to make it easy to find while generally keeping the folder out of sight during normal development. +From time-to-time, generally when making a specification release, the `.design` folder will be cleaned up to remove older documents whose purpose has been served. + +### Requirements discussion + +The purpose of the requirements discussion is to engage the project committers in the discussion about the new feature and to more properly understand the terminology, purpose, use cases, and requirements for the new feature. +A new requirements discussion must be started by first opening a new tracking [issue](https://github.com/osgi/osgi/issues) and labeling the issue with the [`requirements`](https://github.com/osgi/osgi/labels/requirements) label. +Then a new branch should be created from the tip of the `main` branch with the branch name _requirements/XXX_ where _XXX_ is the number of the created tracking issue. + +In this new branch, create a `requirements-XXX.md` (or `requirements-XXX.adoc`) requirements document in the `.design` folder. + +```script +git checkout -b requirements/XXX main +vi .design/requirements-XXX.md +git add .design/requirements-XXX.md +git commit -s -m "First draft of requirements for new Widget specification" +``` + +The requirements document should include the following items for the requirements discussion: + +- Terminology - The new feature may use terminology new to the project committers or that may have multiple meanings. +- Problem Description - What problem or problems will the new feature address or solve? +- Use Cases - Provide several use cases which can demonstrate the actors and how they will use the new feature to address the problem(s). +- Requirements - This is a list of requirements the new feature is to address. + +Once your _requirements/XXX_ branch has your commit with the new requirements document, push this branch your fork and make a pull request to the [main OSGi repository](https://github.com/osgi/osgi). +This pull request will be used to confirm you have signed the [ECA](#legal-considerations) and will be used as the basis for creating the _requirements/XXX_ branch in the main OSGi repository. +A project committer must [create the new _requirements/XXX_ branch](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-and-deleting-branches-within-your-repository#creating-a-branch) in the main OSGi repository and then [change the base branch of the pull request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/changing-the-base-branch-of-a-pull-request) to the newly created _requirements/XXX_ branch in the main OSGi repository. +At this point, the pull request can be merged into the main OSGi repository. + +Discussion on the requirements document can take place in the created issue and updates to the requirements document can be made via additional pull requests against the _requirements/XXX_ branch. + +To successfully conclude the requirements discussion, the project committers must call a [lazy consensus vote](https://community.apache.org/committers/lazyConsensus.html) of the project committers via an email to the `osgi-dev@eclipse.org` mail list. +The lazy consensus vote must remain open for at least 72 hours. + +If the requirements discussion successfully concludes, the _requirements/XXX_ branch is merged into the `main` branch, the tracking issue _XXX_ is closed, and work can then proceed to a design discussion. + +### Design discussion + +The purpose of the design discussion is to engage the project committers in the discussion about the design for a new feature and its place in the overall OSGi architecture. +We want to ensure that sufficient up-front discussion of a new feature design is done in a branch before committing work to the `main` branch. +A new design discussion must be started by first opening a new tracking [issue](https://github.com/osgi/osgi/issues) and labeling the issue with the [`design`](https://github.com/osgi/osgi/labels/design) label. +Then a new branch should be created from the tip of the `main` branch with the branch name _design/XXX_ where _XXX_ is the number of the created design tracking issue. + +In this new branch, create a `design-XXX.md` (or `design-XXX.adoc`) design document in the `.design` folder. + +```script +git checkout -b design/XXX main +vi .design/design-XXX.md +git add .design/design-XXX.md +git commit -s -m "First draft of design for new Widget specification" +``` + +The design document should include the following items for the design discussion: + +- Requirements - This is a list of requirements the new feature is to address. +This can be a reference to a previously created requirements document or a list of requirements that the design will address. +- Technical Solution - What is the design? +This should explain the design and how it works and fits into the OSGi architecture. +This is mainly for discussion within the project and is not necessarily the text that would go in the final specification. +But it is the starting point for that. +- Data Transfer Objects - DTOs are defined and used in many specifications. +Should this new design define any DTOs? + +Once your _design/XXX_ branch has your commit with the new design document, along with any supporting API, code, etc., push this branch your fork and make a pull request to the [main OSGi repository](https://github.com/osgi/osgi). +This pull request will be used to confirm you have signed the [ECA](#legal-considerations) and will be used as the basis for creating the _design/XXX_ branch in the main OSGi repository. +A project committer must [create the new _design/XXX_ branch](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-and-deleting-branches-within-your-repository#creating-a-branch) in the main OSGi repository and then [change the base branch of the pull request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/changing-the-base-branch-of-a-pull-request) to the newly created _design/XXX_ branch in the main OSGi repository. +At this point, the pull request can be merged into the main OSGi repository. + +Discussion on the design document can take place in the created issue and updates to the design document and any supporting code can be made via additional pull requests against the _design/XXX_ branch. + +To successfully conclude the design discussion, the project committers must call a vote of the project committers via an email to the `osgi-dev@eclipse.org` mail list. +The vote must remain open for at least 72 hours. +To succeed, the vote must have at least 3 votes with more `+1` than `-1`. +Any `-1` vote must be accompanied by an explanation of the vote. + +If the design discussion successfully concludes, the _design/XXX_ branch is merged into the `main` branch, the tracking issue _XXX_ is closed, and work can then proceed to specification writing and API and TCK development as well as coordinating the development of a compatible implementation. + +### Create issues + +Any significant improvement should be documented as [a GitHub issue](https://github.com/osgi/osgi/issues) before anybody starts working on it. + +Please take a moment to check that an issue doesn't already exist documenting your bug report or improvement proposal. +If it does, it never hurts to add a quick 👍 reaction. +This will help prioritize the most common problems and requests. + +### Pull requests are always welcome + +We are always thrilled to receive pull requests, and do our best to process them as fast as possible. + +If your pull request is not accepted on the first try, don't be discouraged! +If there's a problem with the implementation, hopefully you received feedback on what to improve. + +### Conventions + +Fork the repository and make changes on your fork in a feature branch: + +- If it's a bug fix branch, name it _issues/XXX_ where XXX is the number of the issue. +- If it's a requirements branch, name it _requirements/XXX_ where XXX is the number of the requirements tracking issue. +- If it's a design branch, name it _design/XXX_ where XXX is the number of the design tracking issue. + +Write clean code. +Universally formatted code promotes ease of writing, reading, and maintenance. +We use Eclipse and each project has Eclipse `.settings` which will properly format the code. +Make sure to avoid unnecessary white space changes which complicate diffs and make reviewing pull requests much more time consuming. + +Pull requests descriptions should be as clear as possible and include a reference to all the issues that they address. + +Pull requests must not contain commits from other users or branches. + +Commit messages must start with a short summary (max. 50 chars) written in the imperative, followed by an optional, more detailed explanatory text which is separated from the summary by an empty line. + +``` +index: Remove absolute URLs from the OBR index + +The url for the root was missing a trailing slash. Using File.toURI to +create an acceptable url. +``` + +Code review comments may be added to your pull request. +Discuss, then make the suggested modifications and push the amended commits to your feature branch. +Be sure to post a comment to the pull request after pushing. +The new commits will show up in the pull request automatically, but the reviewers may not be notified unless you comment. + +Before the pull request is merged, make sure that you squash your commits into logical units of work using `git rebase -i` and `git push --force`. +After every commit, the test suite should be passing. +Include documentation changes in the same commit so that a revert would remove all traces of the feature or fix. + +Commits that fix or close an issue should include a reference like `Closes #XXX` or `Fixes #XXX`, which will automatically close the issue when merged. + +### Sign your work + +Sign off on your commit in the commit comment footer. +By doing this, you assert original authorship of the commit and that you are permitted to contribute it. +This can be automatically added to your commit by passing `-s` to `git commit`, or by hand adding the following line to the footer of the commit. + +``` +Signed-off-by: Full Name +``` + +Remember, if a blank line is found anywhere after the `Signed-off-by` line, the `Signed-off-by:` will be considered outside of the footer, and will fail the automated Signed-off-by validation. + +It is important that you read and understand the legal considerations found below when signing off or contributing any commit. + +### Merge approval + +The maintainers will review your pull request and, if approved, will merge into the main repository. + +If your pull request was originally a draft, don't forget to remove the draft status to signal to the maintainers that it is ready for review. + +## Legal considerations + +Please read the [Eclipse Foundation policy on accepting contributions via Git](http://wiki.eclipse.org/Development_Resources/Contributing_via_Git). + +Your contribution cannot be accepted unless you have a signed [ECA - Eclipse Foundation Contributor Agreement](http://www.eclipse.org/legal/ECA.php) in place. + +Here is the checklist for contributions to be _acceptable_: + +1. [Create an account at Eclipse](https://dev.eclipse.org/site_login/createaccount.php). +2. Add your GitHub user name in your account settings. +3. [Log into the project's portal](https://projects.eclipse.org/) and sign the ["Eclipse ECA"](https://projects.eclipse.org/user/sign/cla). +4. Ensure that you [_sign-off_](https://wiki.eclipse.org/Development_Resources/Contributing_via_Git#Signing_off_on_a_commit) your Git commits. +5. Ensure that you use the _same_ email address as your Eclipse account in commits. +6. Include the appropriate copyright notice and license at the top of each file. + +Your signing of the ECA will be verified by a webservice called 'ip-validation' that checks the email address that signed-off on your commits has signed the ECA. +**Note**: This service is case-sensitive, so ensure the email that signed the ECA and that signed-off on your commits is the same, down to the case. + +### Copyright Notice and Licensing Requirements + +**It is the responsibility of each contributor to obtain legal advice, and to ensure that their contributions fulfill the legal requirements of their organization. This document is not legal advice.** + +OSGi is licensed under the the Apache License, Version 2.0. +Any previously unlicensed contribution should be released under the same license. + +- If you wish to contribute code under a different license, you must consult with a committer before contributing. +- For any scenario not covered by this document, please discuss the copyright notice and licensing requirements with a committer before contributing. + +The template for the copyright notice and license is as follows: + +```java +/* + * Copyright (c) Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +``` diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..0c16611 --- /dev/null +++ b/NOTICE @@ -0,0 +1,46 @@ +# Notices for osgi + +This content is produced and maintained by the OSGi Specification Project. + + * Project home: https://projects.eclipse.org/projects/technology.osgi + +## Trademarks + +OSGi and the OSGi Logo are trademarks of the Eclipse Foundation. Eclipse and +the Eclipse Logo are registered trademarks of the Eclipse Foundation. + +## Copyright + +All content is the property of the respective authors or their employers. +For more information regarding authorship of content, please consult the +listed source code repository logs. + +## Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Apache License, Version 2.0 which is available at +http://www.apache.org/licenses/LICENSE-2.0 + +SPDX-License-Identifier: Apache-2.0 + +## Source Code + +The project maintains the following source code repositories: + + * https://github.com/osgi/org.osgi.util.function.git + +## Third-party Content + +The Content may include items that have been sourced from third parties as follows: + +JUnit + + * License: CPL-1.0 (JUnit 3), EPL-1.0 (JUnit 4), EPL-2.0 (JUnit 5) + * Project: https://junit.org/ + * Source: https://github.com/junit-team + +Apache Software Foundation + + * License: Apache-2.0 + * Project: https://apache.org/ + * Source: https://github.com/apache/ diff --git a/README.md b/README.md index 438c856..488f23a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,85 @@ # org.osgi.util.function + OSGi Specification repo for org.osgi.util.function + +Part of the [OSGi Specification Project](https://projects.eclipse.org/projects/technology.osgi). + +## Compatible implementations + +Implementations known to provide this specification. Additions and +corrections are welcome — please open a pull request. + +| Project | Link | Notes | +|---------|------|-------| +| _none listed yet_ | | | + +## Build + +``` +mvn clean verify +``` + +The `build` workflow builds every push and pull request the same way; every +push to `main` deploys a fresh SNAPSHOT to Sonatype Central snapshots, and +a weekly schedule keeps it fresh in quiet times. + +## Versioning and releases + +The version lives in exactly one place: the `-Drevision=` line in +[`.mvn/maven.config`](.mvn/maven.config). The poms only reference +`${revision}` — never add a second copy of the version anywhere else. + +A release is the creation of a git tag whose name equals that version, +without any prefix: `1.2.3`, not `v1.2.3`. + +1. Open a pull request that sets `-Drevision=1.2.3` in `.mvn/maven.config` + and merge it. +2. Go to **Releases → Draft a new release**, type `1.2.3` under + "Choose a tag" and select "Create new tag on publish". A saved draft + does not create the tag yet, so a draft is a safe intermediate state. +3. Leave **Target: `main`** (the default) — the tag will point at the + merge commit. +4. Click **Publish release**. Publishing creates the tag, and the tag + triggers the release workflow. + +The command-line equivalent is +`gh release create 1.2.3 --target main --generate-notes`. + +The release workflow refuses a tag that does not exactly match +`.mvn/maven.config`, and refuses `-SNAPSHOT` versions. It builds and tests +the repository, then stages the signed artifacts to Maven Central — the +final publish stays a manual step in the Central portal. + +## History + +This repository was extracted from the OSGi monorepo with `git filter-repo`. +It carries the complete history of every file it contains, including the +history from before any rename inside the monorepo. + +A single commit moves the files into the Maven layout (`src` → +`src/main/java`, TCK projects under `tck/`, specification chapters under +`spec/`). Since Git follows paths rather than files, `git log ` shows +only that one commit. To see the whole story: + +``` +git log --follow +``` + +`git blame` follows the renames on its own. The unfiltered monorepo remains +archived at [osgi/osgi](https://github.com/osgi/osgi/). + +## Contact + +- Project contact page: +- Mailing lists: + - [osgi-dev](https://accounts.eclipse.org/mailing-list/osgi-dev) — + developer list for discussion of the OSGi Specification Project + - [osgi-users](https://accounts.eclipse.org/mailing-list/osgi-users) — + public list for discussion of OSGi technology and specifications; ask + your technical questions about OSGi here + - [osgi-wg](https://accounts.eclipse.org/mailing-list/osgi-wg) — + OSGi Working Group mail list +- Slack: +- Spec call: Zoom call every **second Wednesday** — see the + [calendar](https://calendar.google.com/calendar/u/0/newembed?src=c_fh3lhb5p0l29f6phu2ndifh4a4@group.calendar.google.com) + for the exact time (please mind your local timezone). diff --git a/api/bnd.bnd b/api/bnd.bnd new file mode 100644 index 0000000..e4f291a --- /dev/null +++ b/api/bnd.bnd @@ -0,0 +1,4 @@ +# Maven build: the bnd workspace includes were replaced by the +# bnd-maven-plugin configuration in org.osgi.maven.pom.parent. +# Package versions come from the @Version annotations in package-info.java. +Export-Package: ${project.artifactId}.*; -split-package:=first diff --git a/api/pom.xml b/api/pom.xml new file mode 100644 index 0000000..75363cd --- /dev/null +++ b/api/pom.xml @@ -0,0 +1,45 @@ + + 4.0.0 + + org.osgi + org.osgi.util.function.reactor + ${revision} + + org.osgi.util.function + + + + org.osgi + org.osgi.annotation.versioning + 1.1.2 + compile + + + org.osgi + org.osgi.annotation.bundle + 2.0.0 + compile + + + org.osgi + org.osgi.framework + 1.10.0 + provided + + + + + + + biz.aQute.bnd + bnd-maven-plugin + + + biz.aQute.bnd + bnd-baseline-maven-plugin + + + + diff --git a/api/src/main/java/org/osgi/util/function/Consumer.java b/api/src/main/java/org/osgi/util/function/Consumer.java new file mode 100644 index 0000000..de119bd --- /dev/null +++ b/api/src/main/java/org/osgi/util/function/Consumer.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (c) Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ + +package org.osgi.util.function; + +import static java.util.Objects.requireNonNull; + +import org.osgi.annotation.versioning.ConsumerType; + +/** + * A function that accepts a single argument and produces no result. + *

+ * This is a functional interface and can be used as the assignment target for a + * lambda expression or method reference. + * + * @param The type of the function input. + * @ThreadSafe + * @since 1.1 + * @author $Id$ + */ +@ConsumerType +@FunctionalInterface +public interface Consumer { + /** + * Applies this function to the specified argument. + * + * @param t The input to this function. + * @throws Exception An exception thrown by the method. + */ + void accept(T t) throws Exception; + + /** + * Compose the specified {@code Consumer} to be called after this + * {@code Consumer}. + * + * @param after The {@code Consumer} to be called after this + * {@code Consumer} is called. Must not be {@code null}. + * @return A {@code Consumer} composed of this {@code Consumer} and the + * specified {@code Consumer}. + */ + default Consumer andThen(Consumer< ? super T> after) { + requireNonNull(after); + return t -> { + accept(t); + after.accept(t); + }; + } + + /** + * Returns a {@code java.util.function.Consumer} which wraps the specified + * {@code Consumer} and throws any thrown exceptions. + *

+ * The returned {@code java.util.function.Consumer} will throw any exception + * thrown by the wrapped {@code Consumer}. + * + * @param The type of the function input. + * @param wrapped The {@code Consumer} to wrap. Must not be {@code null}. + * @return A {@code java.util.function.Consumer} which wraps the specified + * {@code Consumer}. + */ + static java.util.function.Consumer asJavaConsumer( + Consumer wrapped) { + requireNonNull(wrapped); + return t -> { + try { + wrapped.accept(t); + } catch (Exception e) { + throw Exceptions.throwUnchecked(e); + } + }; + } + + /** + * Returns a {@code java.util.function.Consumer} which wraps the specified + * {@code Consumer} and discards any thrown {@code Exception}s. + *

+ * The returned {@code java.util.function.Consumer} will discard any + * {@code Exception} thrown by the wrapped {@code Consumer}. + * + * @param The type of the function input. + * @param wrapped The {@code Consumer} to wrap. Must not be {@code null}. + * @return A {@code java.util.function.Consumer} which wraps the specified + * {@code Consumer}. + */ + static java.util.function.Consumer asJavaConsumerIgnoreException( + Consumer wrapped) { + requireNonNull(wrapped); + return t -> { + try { + wrapped.accept(t); + } catch (Exception e) { + // discard + } + }; + } + + /** + * Returns a {@code Consumer} which wraps a + * {@code java.util.function.Consumer}. + * + * @param The type of the function input. + * @param wrapped The {@code java.util.function.Consumer} to wrap. Must not + * be {@code null}. + * @return A {@code Consumer} which wraps the specified + * {@code java.util.function.Consumer}. + */ + static Consumer asConsumer(java.util.function.Consumer wrapped) { + requireNonNull(wrapped); + return wrapped::accept; + } +} diff --git a/api/src/main/java/org/osgi/util/function/Exceptions.java b/api/src/main/java/org/osgi/util/function/Exceptions.java new file mode 100644 index 0000000..4f7e2e5 --- /dev/null +++ b/api/src/main/java/org/osgi/util/function/Exceptions.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ + +package org.osgi.util.function; + +class Exceptions { + private Exceptions() { + } + + static RuntimeException throwUnchecked(Throwable t) { + throwsUnchecked(t); + throw new AssertionError("unreachable"); + } + + @SuppressWarnings("unchecked") + private static void throwsUnchecked( + Throwable throwable) throws UNCHECKED { + throw (UNCHECKED) throwable; + } +} diff --git a/api/src/main/java/org/osgi/util/function/Function.java b/api/src/main/java/org/osgi/util/function/Function.java new file mode 100644 index 0000000..76cca81 --- /dev/null +++ b/api/src/main/java/org/osgi/util/function/Function.java @@ -0,0 +1,179 @@ +/******************************************************************************* + * Copyright (c) Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ + +package org.osgi.util.function; + +import static java.util.Objects.requireNonNull; + +import org.osgi.annotation.versioning.ConsumerType; + +/** + * A function that accepts a single argument and produces a result. + *

+ * This is a functional interface and can be used as the assignment target for a + * lambda expression or method reference. + * + * @param The type of the function input. + * @param The type of the function output. + * @ThreadSafe + * @author $Id$ + */ +@ConsumerType +@FunctionalInterface +public interface Function { + /** + * Applies this function to the specified argument. + * + * @param t The input to this function. + * @return The output of this function. + * @throws Exception An exception thrown by the method. + */ + R apply(T t) throws Exception; + + /** + * Compose the specified {@code Function} to be called on the value returned + * by this {@code Function}. + * + * @param The type of the value supplied by the specified + * {@code Function}. + * @param after The {@code Function} to be called on the value returned by + * this {@code Function}. Must not be {@code null}. + * @return A {@code Function} composed of this {@code Function} and the + * specified {@code Function}. + */ + default Function andThen(Function< ? super R, ? extends S> after) { + requireNonNull(after); + return t -> after.apply(apply(t)); + } + + /** + * Compose the specified {@code Function} to be called to supply a value to + * be consumed by this {@code Function}. + * + * @param The type of the value consumed the specified {@code Function}. + * @param before The {@code Function} to be called to supply a value to be + * consumed by this {@code Function}. Must not be {@code null}. + * @return A {@code Function} composed of this {@code Function} and the + * specified {@code Function}. + */ + default Function compose( + Function< ? super S, ? extends T> before) { + requireNonNull(before); + return s -> apply(before.apply(s)); + } + + /** + * Returns a {@code java.util.function.Function} which wraps the specified + * {@code Function} and throws any thrown exceptions. + *

+ * The returned {@code java.util.function.Function} will throw any exception + * thrown by the wrapped {@code Function}. + * + * @param The type of the function input. + * @param The type of the function output. + * @param wrapped The {@code Function} to wrap. Must not be {@code null}. + * @return A {@code java.util.function.Function} which wraps the specified + * {@code Function}. + */ + static java.util.function.Function asJavaFunction( + Function wrapped) { + requireNonNull(wrapped); + return t -> { + try { + return wrapped.apply(t); + } catch (Exception e) { + throw Exceptions.throwUnchecked(e); + } + }; + } + + /** + * Returns a {@code java.util.function.Function} which wraps the specified + * {@code Function} and the specified value. + *

+ * If the the specified {@code Function} throws an {@code Exception}, the + * the specified value is returned. + * + * @param The type of the function input. + * @param The type of the function output. + * @param wrapped The {@code Function} to wrap. Must not be {@code null}. + * @param orElse The value to return if the specified {@code Function} + * throws an {@code Exception}. + * @return A {@code java.util.function.Function} which wraps the specified + * {@code Function} and the specified value. + */ + static java.util.function.Function asJavaFunctionOrElse( + Function wrapped, R orElse) { + requireNonNull(wrapped); + return t -> { + try { + return wrapped.apply(t); + } catch (Exception e) { + return orElse; + } + }; + } + + /** + * Returns a {@code java.util.function.Function} which wraps the specified + * {@code Function} and the specified {@code java.util.function.Supplier}. + *

+ * If the the specified {@code Function} throws an {@code Exception}, the + * value returned by the specified {@code java.util.function.Supplier} is + * returned. + * + * @param The type of the function input. + * @param The type of the function output. + * @param wrapped The {@code Function} to wrap. Must not be {@code null}. + * @param orElseGet The {@code java.util.function.Supplier} to call for a + * return value if the specified {@code Function} throws an + * {@code Exception}. + * @return A {@code java.util.function.Function} which wraps the specified + * {@code Function} and the specified + * {@code java.util.function.Supplier}. + */ + static java.util.function.Function asJavaFunctionOrElseGet( + Function wrapped, + java.util.function.Supplier< ? extends R> orElseGet) { + requireNonNull(wrapped); + return t -> { + try { + return wrapped.apply(t); + } catch (Exception e) { + return orElseGet.get(); + } + }; + } + + /** + * Returns a {@code Function} which wraps the specified + * {@code java.util.function.Function}. + * + * @param The type of the function input. + * @param The type of the function output. + * @param wrapped The {@code java.util.function.Function} to wrap. Must not + * be {@code null}. + * @return A {@code Function} which wraps the specified + * {@code java.util.function.Function}. + */ + static Function asFunction( + java.util.function.Function wrapped) { + requireNonNull(wrapped); + return wrapped::apply; + } +} diff --git a/api/src/main/java/org/osgi/util/function/Predicate.java b/api/src/main/java/org/osgi/util/function/Predicate.java new file mode 100644 index 0000000..9b23069 --- /dev/null +++ b/api/src/main/java/org/osgi/util/function/Predicate.java @@ -0,0 +1,189 @@ +/******************************************************************************* + * Copyright (c) Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ + +package org.osgi.util.function; + +import static java.util.Objects.requireNonNull; + +import org.osgi.annotation.versioning.ConsumerType; + +/** + * A predicate that accepts a single argument and produces a boolean result. + *

+ * This is a functional interface and can be used as the assignment target for a + * lambda expression or method reference. + * + * @param The type of the predicate input. + * @ThreadSafe + * @author $Id$ + */ +@ConsumerType +@FunctionalInterface +public interface Predicate { + /** + * Evaluates this predicate on the specified argument. + * + * @param t The input to this predicate. + * @return {@code true} if the specified argument is accepted by this + * predicate; {@code false} otherwise. + * @throws Exception An exception thrown by the method. + */ + boolean test(T t) throws Exception; + + /** + * Return a {@code Predicate} which is the negation of this + * {@code Predicate}. + * + * @return A {@code Predicate} which is the negation of this + * {@code Predicate}. + */ + default Predicate negate() { + return t -> !test(t); + } + + /** + * Compose this {@code Predicate} logical-AND the specified + * {@code Predicate}. + *

+ * Short-circuiting is used, so the specified {@code Predicate} is not + * called if this {@code Predicate} returns {@code false}. + * + * @param and The {@code Predicate} to be called after this + * {@code Predicate} is called. Must not be {@code null}. + * @return A {@code Predicate} composed of this {@code Predicate} and the + * specified {@code Predicate} using logical-AND. + */ + default Predicate and(Predicate< ? super T> and) { + requireNonNull(and); + return t -> test(t) && and.test(t); + } + + /** + * Compose this {@code Predicate} logical-OR the specified + * {@code Predicate}. + *

+ * Short-circuiting is used, so the specified {@code Predicate} is not + * called if this {@code Predicate} returns {@code true}. + * + * @param or The {@code Predicate} to be called after this {@code Predicate} + * is called. Must not be {@code null}. + * @return A {@code Predicate} composed of this {@code Predicate} and the + * specified {@code Predicate} using logical-OR. + */ + default Predicate or(Predicate< ? super T> or) { + requireNonNull(or); + return t -> test(t) || or.test(t); + } + + /** + * Returns a {@code java.util.function.Predicate} which wraps the specified + * {@code Predicate} and throws any thrown exceptions. + *

+ * The returned {@code java.util.function.Predicate} will throw any + * exception thrown by the wrapped {@code Predicate}. + * + * @param The type of the predicate input. + * @param wrapped The {@code Predicate} to wrap. Must not be {@code null}. + * @return A {@code java.util.function.Predicate} which wraps the specified + * {@code Predicate}. + */ + static java.util.function.Predicate asJavaPredicate( + Predicate wrapped) { + requireNonNull(wrapped); + return t -> { + try { + return wrapped.test(t); + } catch (Exception e) { + throw Exceptions.throwUnchecked(e); + } + }; + } + + /** + * Returns a {@code java.util.function.Predicate} which wraps the specified + * {@code Predicate} and the specified value. + *

+ * If the the specified {@code Predicate} throws an {@code Exception}, the + * the specified value is returned. + * + * @param The type of the predicate input. + * @param wrapped The {@code Predicate} to wrap. Must not be {@code null}. + * @param orElse The value to return if the specified {@code Predicate} + * throws an {@code Exception}. + * @return A {@code java.util.function.Predicate} which wraps the specified + * {@code Predicate} and the specified value. + */ + static java.util.function.Predicate asJavaPredicateOrElse( + Predicate wrapped, boolean orElse) { + requireNonNull(wrapped); + return t -> { + try { + return wrapped.test(t); + } catch (Exception e) { + return orElse; + } + }; + } + + /** + * Returns a {@code java.util.function.Predicate} which wraps the specified + * {@code Predicate} and the specified + * {@code java.util.function.BooleanSupplier}. + *

+ * If the the specified {@code Predicate} throws an {@code Exception}, the + * value returned by the specified + * {@code java.util.function.BooleanSupplier} is returned. + * + * @param The type of the predicate input. + * @param wrapped The {@code Predicate} to wrap. Must not be {@code null}. + * @param orElseGet The {@code java.util.function.BooleanSupplier} to call + * for a return value if the specified {@code Predicate} throws + * an {@code Exception}. + * @return A {@code java.util.function.Predicate} which wraps the specified + * {@code Predicate} and the specified + * {@code java.util.function.BooleanSupplier}. + */ + static java.util.function.Predicate asJavaPredicateOrElseGet( + Predicate wrapped, + java.util.function.BooleanSupplier orElseGet) { + requireNonNull(wrapped); + return t -> { + try { + return wrapped.test(t); + } catch (Exception e) { + return orElseGet.getAsBoolean(); + } + }; + } + + /** + * Returns a {@code Predicate} which wraps the specified + * {@code java.util.function.Predicate}. + * + * @param The type of the predicate input. + * @param wrapped The {@code java.util.function.Predicate} to wrap. Must not + * be {@code null}. + * @return A {@code Predicate} which wraps the specified + * {@code java.util.function.Predicate}. + */ + static Predicate asPredicate( + java.util.function.Predicate wrapped) { + requireNonNull(wrapped); + return wrapped::test; + } +} diff --git a/api/src/main/java/org/osgi/util/function/Supplier.java b/api/src/main/java/org/osgi/util/function/Supplier.java new file mode 100644 index 0000000..ff7dbe5 --- /dev/null +++ b/api/src/main/java/org/osgi/util/function/Supplier.java @@ -0,0 +1,140 @@ +/******************************************************************************* + * Copyright (c) Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ + +package org.osgi.util.function; + +import static java.util.Objects.requireNonNull; + +import org.osgi.annotation.versioning.ConsumerType; + +/** + * A function that produces a result. + *

+ * This is a functional interface and can be used as the assignment target for a + * lambda expression or method reference. + * + * @param The type of the function output. + * @ThreadSafe + * @author $Id$ + */ +@ConsumerType +@FunctionalInterface +public interface Supplier { + /** + * Returns a value. + * + * @return The output of this function. + * @throws Exception An exception thrown by the method. + */ + T get() throws Exception; + + /** + * Returns a {@code java.util.function.Supplier} which wraps the specified + * {@code Supplier} and throws any thrown exceptions. + *

+ * The returned {@code java.util.function.Supplier} will throw any exception + * thrown by the wrapped {@code Supplier}. + * + * @param The type of the function output. + * @param wrapped The {@code Supplier} to wrap. Must not be {@code null}. + * @return A {@code java.util.function.Supplier} which wraps the specified + * {@code Supplier}. + */ + static java.util.function.Supplier asJavaSupplier( + Supplier wrapped) { + requireNonNull(wrapped); + return () -> { + try { + return wrapped.get(); + } catch (Exception e) { + throw Exceptions.throwUnchecked(e); + } + }; + } + + /** + * Returns a {@code java.util.function.Supplier} which wraps the specified + * {@code Supplier} and the specified value. + *

+ * If the the specified {@code Supplier} throws an {@code Exception}, the + * the specified value is returned. + * + * @param The type of the function output. + * @param wrapped The {@code Supplier} to wrap. Must not be {@code null}. + * @param orElse The value to return if the specified {@code Supplier} + * throws an {@code Exception}. + * @return A {@code java.util.function.Supplier} which wraps the specified + * {@code Supplier} and the specified value. + */ + static java.util.function.Supplier asJavaSupplierOrElse( + Supplier wrapped, T orElse) { + requireNonNull(wrapped); + return () -> { + try { + return wrapped.get(); + } catch (Exception e) { + return orElse; + } + }; + } + + /** + * Returns a {@code java.util.function.Supplier} which wraps the specified + * {@code Supplier} and the specified {@code java.util.function.Supplier}. + *

+ * If the the specified {@code Supplier} throws an {@code Exception}, the + * value returned by the specified {@code java.util.function.Supplier} is + * returned. + * + * @param The type of the function output. + * @param wrapped The {@code Supplier} to wrap. Must not be {@code null}. + * @param orElseGet The {@code java.util.function.Supplier} to call for a + * return value if the specified {@code Supplier} throws an + * {@code Exception}. + * @return A {@code java.util.function.Supplier} which wraps the specified + * {@code Supplier} and the specified + * {@code java.util.function.Supplier}. + */ + static java.util.function.Supplier asJavaSupplierOrElseGet( + Supplier wrapped, + java.util.function.Supplier< ? extends T> orElseGet) { + requireNonNull(wrapped); + return () -> { + try { + return wrapped.get(); + } catch (Exception e) { + return orElseGet.get(); + } + }; + } + + /** + * Returns a {@code Supplier} which wraps the specified + * {@code java.util.function.Supplier}. + * + * @param The type of the function output. + * @param wrapped The {@code java.util.function.Supplier} to wrap. Must not + * be {@code null}. + * @return A {@code Supplier} which wraps the specified + * {@code java.util.Supplier.Function}. + */ + static Supplier asSupplier(java.util.function.Supplier wrapped) { + requireNonNull(wrapped); + return wrapped::get; + } +} diff --git a/api/src/main/java/org/osgi/util/function/package-info.java b/api/src/main/java/org/osgi/util/function/package-info.java new file mode 100644 index 0000000..7088a5c --- /dev/null +++ b/api/src/main/java/org/osgi/util/function/package-info.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ + +/** + * Function Package Version 1.2. + *

+ * Bundles wishing to use this package must list the package in the + * Import-Package header of the bundle's manifest. + *

+ * Example import for consumers using the API in this package: + *

+ * {@code Import-Package: org.osgi.util.function; version="[1.2,2.0)"} + *

+ * Example import for providers implementing the API in this package: + *

+ * {@code Import-Package: org.osgi.util.function; version="[1.2,1.3)"} + * + * @author $Id$ + */ + +@Version("1.2") +package org.osgi.util.function; + +import org.osgi.annotation.versioning.Version; + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..7882f14 --- /dev/null +++ b/pom.xml @@ -0,0 +1,74 @@ + + + 4.0.0 + org.osgi + org.osgi.util.function.reactor + ${revision} + pom + OSGi Specification repo for org.osgi.util.function + + + + org.osgi + org.osgi.maven.pom.parent + 0.0.1-SNAPSHOT + + + + + + central-snapshots + Central Snapshots + https://central.sonatype.com/repository/maven-snapshots/ + + false + + + true + + + + + + + central-snapshots + Central Snapshots + https://central.sonatype.com/repository/maven-snapshots/ + + false + + + true + + + + + + api + + + + + + + org.codehaus.mojo + flatten-maven-plugin + + + +