{"id":37016158,"url":"https://github.com/vinayalodha/elvis","last_synced_at":"2026-01-14T01:48:45.672Z","repository":{"id":40312915,"uuid":"386241553","full_name":"vinayalodha/elvis","owner":"vinayalodha","description":"Java Compiler plugin for Null safety ","archived":false,"fork":false,"pushed_at":"2025-09-30T09:05:43.000Z","size":436,"stargazers_count":18,"open_issues_count":15,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-09-30T11:22:30.828Z","etag":null,"topics":["java","null-safety"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vinayalodha.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-07-15T09:55:06.000Z","updated_at":"2025-04-12T07:21:56.000Z","dependencies_parsed_at":"2024-04-04T21:24:23.459Z","dependency_job_id":"035f8016-12a0-4770-9965-71387154ee6a","html_url":"https://github.com/vinayalodha/elvis","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/vinayalodha/elvis","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vinayalodha%2Felvis","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vinayalodha%2Felvis/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vinayalodha%2Felvis/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vinayalodha%2Felvis/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vinayalodha","download_url":"https://codeload.github.com/vinayalodha/elvis/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vinayalodha%2Felvis/sbom","scorecard":{"id":168369,"data":{"date":"2025-08-04","repo":{"name":"github.com/vinayalodha/elvis","commit":"8820d8a804b7e5e9ba37e2b3f2ba15113c5616f1"},"scorecard":{"version":"v5.2.1-28-gc1d103a9","commit":"c1d103a9bb9f635ec7260bf9aa0699466fa4be0e"},"score":4.7,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#maintained"}},{"name":"Code-Review","score":0,"reason":"Found 0/14 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":9,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: jobLevel 'actions' permission set to 'read': .github/workflows/codeql-analysis.yml:28","Info: jobLevel 'contents' permission set to 'read': .github/workflows/codeql-analysis.yml:29","Warn: no topLevel permission defined: .github/workflows/codeql-analysis.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:42: update your workflow using https://app.stepsecurity.io/secureworkflow/vinayalodha/elvis/codeql-analysis.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:46: update your workflow using https://app.stepsecurity.io/secureworkflow/vinayalodha/elvis/codeql-analysis.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:57: update your workflow using https://app.stepsecurity.io/secureworkflow/vinayalodha/elvis/codeql-analysis.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:71: update your workflow using https://app.stepsecurity.io/secureworkflow/vinayalodha/elvis/codeql-analysis.yml/main?enable=pin","Info:   0 out of   4 GitHub-owned GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#branch-protection"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#license"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":7,"reason":"SAST tool detected but not run on all commits","details":["Info: SAST configuration detected: CodeQL","Warn: 0 commits out of 16 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-16T15:35:54.822Z","repository_id":40312915,"created_at":"2025-08-16T15:35:54.822Z","updated_at":"2025-08-16T15:35:54.822Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28408689,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T00:40:43.272Z","status":"ssl_error","status_checked_at":"2026-01-14T00:40:42.636Z","response_time":56,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["java","null-safety"],"created_at":"2026-01-14T01:48:44.399Z","updated_at":"2026-01-14T01:48:45.666Z","avatar_url":"https://github.com/vinayalodha.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Elvis Operator for Java (Java Compiler plugin)\n\nOne way of writing null safe code in Java is using `Optional.ofNullable`, Even though I feel it is a better alternative\nto writing `if` it gets repetitive and verbose.\n\nI have written `Optional.ofNullable` countless number of times, and I don't want to write it anymore :)\n\nThis project aims to mitigate those problems using Java Compiler Plugin API.\n\n## Usage\n\nConsider you need to fetch `cardNumber` from `order` object, given order and its nested objects can be null, code\nsnippet would look something like below\n\n### Java 7\n\n    public String getCardNumberFromOrder(Order order) {\n        if(order != null \n                \u0026\u0026 order.getPayment() != null\n                \u0026\u0026 order.getPayment().getCard() != null) {\n            return order.getPayment().getCard().getCardNumber();\n        }\n        return null;\n    }\n\n### Java 8\n\nFunctional way with Java 8\n\n    public String getCardNumberFromOrder(Order order) {\n        return Optional.ofNullable(order)\n            .map(Order::getPayment)\n            .map(Payment::getCard)\n            .map(Card::getCardNumber)\n            .orElse(null);\n    }\n\n### Elvis Plugin\n\n    public String getCardNumberFromOrder(Order order) {\n        @NullSafe\n        String cardNumber = order.getPayment().getCard().getCardNumber();\n        return cardNumber;\n    }\n\nIf you open class file for this code it will look something like below\n\n    public String getCardNumberFromOrder(Order order) {\n        return Optional.ofNullable(order)\n            .map(it -\u003e it.getPayment())\n            .map(it -\u003e it.getCard())\n            .map(it -\u003e it.getCardNumber())\n            .orElse(null);\n    }\n\nThis is done using AST manipulation during compile time.\n\nIf you want to assign default value to cardNumber in above example, then code will look something like below\n\n    public String getCardNumberFromOrder(Order order) {\n        @NullSafe(\"1111222233334444\")\n        String cardNumber = order.getPayment().getCard().getCardNumber();\n        return cardNumber;\n    }\n\nFor more examples please\nrefer [here](./plugin-test/src/test/java/io/github/vinayalodha/elvis/plugin/test/positive/AstTransformationTests.java)\nand [here](./plugin-test/src/test/java/io/github/vinayalodha/elvis/plugin/test/positive/)\n.\n\n## Installation\n\n### Maven configuration\n\nYou need to add `-Xplugin:ElvisPlugin` in compiler arguments\n\n    \u003cdependencies\u003e\n        \u003cdependency\u003e\n            \u003cgroupId\u003eio.github.vinayalodha\u003c/groupId\u003e\n            \u003cartifactId\u003eelvis-plugin\u003c/artifactId\u003e\n            \u003cversion\u003e1.0.3\u003c/version\u003e\n            \u003cscope\u003eprovided\u003c/scope\u003e\n        \u003c/dependency\u003e\n    \u003c/dependencies\u003e\n    \u003cplugin\u003e\n        \u003cgroupId\u003eorg.apache.maven.plugins\u003c/groupId\u003e\n            \u003cartifactId\u003emaven-compiler-plugin\u003c/artifactId\u003e\n            \u003cversion\u003e3.8.1\u003c/version\u003e\n            \u003cconfiguration\u003e\n                \u003csource\u003e11\u003c/source\u003e\n                \u003ctarget\u003e11\u003c/target\u003e\n                \u003ccompilerArgs\u003e\n                    \u003carg\u003e-Xplugin:ElvisPlugin\u003c/arg\u003e\n                \u003c/compilerArgs\u003e\n        \u003c/configuration\u003e\n    \u003c/plugin\u003e\n\nFor JDK 16 you need to set `\u003cfork\u003etrue\u003c/fork\u003e` and add additional args in \u003ccompilerArgs\u003e tag. Please refer\nto [JSR](https://openjdk.java.net/jeps/396)\n\n    \u003carg\u003e-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED\u003c/arg\u003e\n    \u003carg\u003e-J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED\u003c/arg\u003e\n    \u003carg\u003e-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED\u003c/arg\u003e\n    \u003carg\u003e-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED\u003c/arg\u003e\n    \u003carg\u003e-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED\u003c/arg\u003e\n\n### IntelliJ Idea\n\nYou need to add `-Xplugin:ElvisPlugin` in compiler arguments\n\n![Intellij Setting](./docs/Intellj%20Idea%20setting.png)\n\n## Current limitations\n\n### Works only on local variable\n\nCurrently, @NotNull can only be used on local variable, this is due to fact Java annotation limit target type for\nannotation to local variable\n\nfor example\n\n    public String getCardNumberFromOrder(Order order) {\n        @NullSafe\n        String cardNumber = order.getPayment().getCard().getCardNumber();\n        return cardNumber;\n    }\n\ncan not be written as\n\n    public String getCardNumberFromOrder(Order order) {\n        @NullSafe\n        return order.getPayment().getCard().getCardNumber(); // In Java you cant have annotation on return statement\n    }\n\n### If you have classname in expression then you will get compilation error\n\nFor example, below code will give compilation error, given it contain classname(StringUtils) in expression\n\n    public String getCardNumberFromOrder(Order order) {\n        @NullSafe\n        String cardNumber = StringUtils.INSTANCE.trim();\n        return cardNumber;\n    }\n\nAbove code will throw compilation error saying `Unable to find Symbol StringUtils`\n\n### If expression have Checked exception it will give compilation error\n\nConsider example\n\n    public String getSomeStuff(SomeClass object) {\n        @NullSafe\n        String obj = object.thisMethodWillThrowIoException();\n        return obj;\n    }\n\nHere after AST manipulation by this plugin code will become\n\n    public String getSomeStuff(SomeClass object) {\n        String obj = Optional.ofNullable(object)\n                        .map(it -\u003e it.thisMethodWillThrowIoException()) // In Java you can't have checked exception in lambda\n                        .orElse(null);\n        return obj;\n    }\n\nGiven Java Stream won't allow checked exception in lambda, you will get compilation error saying\n\n    java: unreported exception java.io.IOException; must be caught or declared to be thrown\n\n### Does not work with eclipse\n\nEclipse use its own compiler ECJ(Eclipse compiler for Java), and I could not find any way to pass\nin `-Xplugin:ElvisPlugin`. I am still trying to find way where I can patch this.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvinayalodha%2Felvis","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvinayalodha%2Felvis","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvinayalodha%2Felvis/lists"}