├── .github └── workflows │ ├── gradle-publish.yml │ └── gradle.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── codestyle ├── checkstyle.xml ├── idea_code_style.xml ├── pmd_main.xml └── pmd_test.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── main └── java │ └── com │ └── kirekov │ └── sneaky │ ├── Sneaky.java │ └── lambda │ ├── CheckedBiConsumer.java │ ├── CheckedBiFunction.java │ ├── CheckedBiPredicate.java │ ├── CheckedConsumer.java │ ├── CheckedFunction.java │ ├── CheckedPredicate.java │ └── CheckedSupplier.java └── test └── java └── com └── kirekov └── sneaky └── SneakyTest.java /.github/workflows/gradle-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # This workflow will build a package using Gradle and then publish it to GitHub packages when a release is created 6 | # For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#Publishing-using-gradle 7 | 8 | name: Gradle Package 9 | 10 | on: 11 | push: 12 | branches: 13 | - master 14 | - releases 15 | 16 | jobs: 17 | build: 18 | 19 | runs-on: ubuntu-latest 20 | permissions: 21 | contents: read 22 | packages: write 23 | 24 | steps: 25 | - uses: actions/checkout@v2 26 | - name: Set up JDK 8 27 | uses: actions/setup-java@v2 28 | with: 29 | java-version: '8' 30 | distribution: 'temurin' 31 | server-id: github # Value of the distributionManagement/repository/id field of the pom.xml 32 | settings-path: ${{ github.workspace }} # location for the settings.xml file 33 | 34 | - name: Prepare to publish 35 | run: | 36 | echo "${{ secrets.GPG_KEY_CONTENTS }}" | base64 --decode > private_key.gpg 37 | cat ./private_key.gpg | sha256sum 38 | - name: Publish with Gradle 39 | uses: eskatos/gradle-command-action@v1 40 | with: 41 | gradle-version: current 42 | arguments: build publishToSonatype closeAndReleaseSonatypeStagingRepository -Psigning.secretKeyRingFile=private_key.gpg -Psigning.keyId=${{secrets.SIGNING_KEYID}} -Psigning.password=${{secrets.SIGNING_PASSWORD}} -PossSonatypeUsername=${{secrets.OSS_SONATYPE_USERNAME}} -PossSonatypePassword=${{secrets.OSS_SONATYPE_PASSWORD}} 43 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time 6 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle 7 | 8 | name: Java CI with Gradle 9 | 10 | on: 11 | push: 12 | branches: 13 | - master 14 | - releases 15 | pull_request: 16 | branches: 17 | - master 18 | - releases 19 | 20 | jobs: 21 | build: 22 | 23 | runs-on: ubuntu-latest 24 | 25 | steps: 26 | - uses: actions/checkout@v2 27 | - name: Set up JDK 11 28 | uses: actions/setup-java@v2 29 | with: 30 | java-version: '11' 31 | distribution: 'temurin' 32 | - name: Cache SonarCloud packages 33 | uses: actions/cache@v1 34 | with: 35 | path: ~/.sonar/cache 36 | key: ${{ runner.os }}-sonar 37 | restore-keys: ${{ runner.os }}-sonar 38 | - name: Cache Gradle packages 39 | uses: actions/cache@v1 40 | with: 41 | path: ~/.gradle/caches 42 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} 43 | restore-keys: ${{ runner.os }}-gradle 44 | - name: Build with Gradle 45 | uses: gradle/gradle-build-action@937999e9cc2425eddc7fd62d1053baf041147db7 46 | env: 47 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any 48 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 49 | with: 50 | arguments: build jacocoTestReport sonarqube 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out/** 2 | target/ 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | pom.xml.next 7 | release.properties 8 | dependency-reduced-pom.xml 9 | buildNumber.properties 10 | .mvn/timing.properties 11 | 12 | # Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) 13 | !/.mvn/wrapper/maven-wrapper.jar 14 | .idea/** 15 | .idea/workspace.xml 16 | /java-useful-utils.iml 17 | .gradle/** 18 | build/** 19 | private_key.gpg 20 | 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Semyon Kirekov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sneaky Java 2 | 3 | Java utilities to throw checked exceptions in a "sneaky" way. 4 | 5 | If you're tired of checked exceptions in lambdas, then this library is made for you! 6 | For example, take a look at this code snippet. 7 | 8 | ```java 9 | class MyClass { 10 | 11 | void doAction() { 12 | List messageContents = getStringLines() 13 | .stream() 14 | .map(str -> { 15 | try { 16 | return objectMapper.readValue(str, MessageDTO.class); 17 | } catch (JsonProccesingException e) { 18 | throw new RuntimeException(e); 19 | } 20 | }) 21 | .map(msg -> msg.getContent()) 22 | .toList(); 23 | } 24 | } 25 | ``` 26 | 27 | It can be simplified with `Sneaky.function`. 28 | 29 | ```java 30 | class MyClass { 31 | 32 | void doAction() { 33 | List messageContents = getStringLines() 34 | .stream() 35 | .map(Sneaky.function( 36 | str -> objectMapper.readValue(str, MessageDTO.class) 37 | )) 38 | .map(msg -> msg.getContent()) 39 | .toList(); 40 | } 41 | } 42 | ``` 43 | 44 | This library has no dependencies. 45 | 46 | The `master` branch provides the latest `DEV-SNAPSHOT` version. You can find the specific release 47 | version info by git tags. 48 | 49 | ## Status 50 | 51 | [![Build Status](https://app.travis-ci.com/SimonHarmonicMinor/sneaky-java.svg?branch=master)](https://app.travis-ci.com/SimonHarmonicMinor/sneaky-java) 52 | [![Javadoc](https://javadoc.io/badge2/com.kirekov/sneaky-java/javadoc.svg)](https://javadoc.io/doc/com.kirekov/sneaky-java) 53 | [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=SimonHarmonicMinor_sneaky-java&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=SimonHarmonicMinor_sneaky-java) 54 | [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=SimonHarmonicMinor_sneaky-java&metric=coverage)](https://sonarcloud.io/summary/new_code?id=SimonHarmonicMinor_sneaky-java) 55 | [![Hits-of-Code](https://hitsofcode.com/github/simonharmonicminor/sneaky-java?branch=master)](https://hitsofcode.com/github/simonharmonicminor/sneaky-java/view?branch=master) 56 | [![checkstyle](https://img.shields.io/badge/checkstyle-intergrated-informational)](https://checkstyle.sourceforge.io/) 57 | [![PMD](https://img.shields.io/badge/PMD-intergrated-informational)](https://pmd.github.io/pmd-6.35.0/pmd_rules_java.html) 58 | [![MIT License](https://img.shields.io/apm/l/atomic-design-ui.svg?)](https://github.com/SimonHarmonicMinor/Java-Useful-Utils/blob/master/LICENSE) 59 | 60 | ## Quick Start 61 | 62 | You need Java 8+ to use the library. 63 | 64 | Please, use the latest release 65 | version [![Maven Central](https://img.shields.io/maven-central/v/com.kirekov/sneaky-java)](https://mvnrepository.com/artifact/com.kirekov/sneaky-java) 66 | . 67 | 68 | Maven: 69 | 70 | ```xml 71 | 72 | 73 | com.kirekov 74 | sneaky-java 75 | x.y.z 76 | 77 | ``` 78 | 79 | Gradle: 80 | 81 | ```groovy 82 | implementation 'com.kirekov:sneaky-java:x.y.z' 83 | ``` 84 | 85 | ## Usage 86 | 87 | The library provides wrappers for native Java functional interfaces. 88 | 89 | Suppose we want to declare a `Predicate` to use it within Stream API. Here we got `isValueAllowed` 90 | method. 91 | 92 | ```java 93 | class Predicates { 94 | 95 | boolean isValueAllowed(String value) throws IOException { 96 | // filtering logic 97 | } 98 | } 99 | ``` 100 | 101 | Sadly, the underlined statement won't compile. 102 | 103 | ```java 104 | class MyService { 105 | 106 | void doJob() { 107 | // compile error happens here 108 | Predicate predicate = (value) -> isValueAllowed(value); 109 | ... 110 | } 111 | } 112 | ``` 113 | 114 | Because `isValueAllowed` may throw `IOException` which is 115 | a [checked exception](https://www.baeldung.com/java-checked-unchecked-exceptions). 116 | Whilst `Predicate` declaration does not allow it. We can write a custom wrapper for this case. 117 | 118 | ```java 119 | class MyService { 120 | 121 | void doJob() { 122 | Predicate predicate = (value) -> { 123 | try { 124 | return isValueAllowed(value); 125 | } catch (IOException e) { 126 | throw new RuntimeException(e); 127 | } 128 | } 129 | ... 130 | } 131 | } 132 | ``` 133 | 134 | Though the solution it does work, it looks rather ugly. Fortunately, `sneaky-java` provides 135 | convenient factory methods for such cases. 136 | 137 | ```java 138 | import com.kirekov.sneaky.Sneaky; 139 | 140 | class MyService { 141 | 142 | void doJob() { 143 | Predicate predicate = Sneaky.predicate( 144 | value -> isValueAllowed(value) 145 | ); 146 | ... 147 | } 148 | } 149 | ``` 150 | 151 | Besides, sneaky predicates does not wrap checked exceptions with `RuntimeException` instance. 152 | Instead, it uses Java generic type erasure mechanism to rethrow checked exception ignoring compile 153 | errors. Here is the core idea. 154 | 155 | ```java 156 | class Sneaky { 157 | 158 | @SuppressWarnings("unchecked") 159 | public static void throwUnchecked(Exception exception) throws T { 160 | throw (T) exception; 161 | } 162 | } 163 | ``` 164 | 165 | The `sneaky-java` provides wrappers for the common Java functional interfaces. 166 | 167 | 1. [Predicate](https://docs.oracle.com/javase/8/docs/api/java/util/function/Predicate.html) 168 | — `Sneaky.predicate` 169 | 2. [BiPredicate](https://docs.oracle.com/javase/8/docs/api/java/util/function/BiPredicate.html) 170 | — `Sneaky.biPredicate` 171 | 3. [Function](https://docs.oracle.com/javase/8/docs/api/java/util/function/Function.html) 172 | — `Sneaky.function` 173 | 4. [BiFunction](https://docs.oracle.com/javase/8/docs/api/java/util/function/BiFunction.html) 174 | — `Sneaky.biFunction` 175 | 5. [Consumer](https://docs.oracle.com/javase/8/docs/api/java/util/function/Consumer.html) 176 | — `Sneaky.consumer` 177 | 6. [BiConsumer](https://docs.oracle.com/javase/8/docs/api/java/util/function/BiConsumer.html) 178 | — `Sneaky.biConsumer` 179 | 7. [Supplier](https://docs.oracle.com/javase/8/docs/api/java/util/function/Supplier.html) 180 | — `Sneaky.supplier` -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'maven-publish' 4 | id 'signing' 5 | id 'jacoco' 6 | id "org.sonarqube" version "3.3" 7 | id 'checkstyle' 8 | id "io.github.gradle-nexus.publish-plugin" version "1.1.0" 9 | id 'pmd' 10 | } 11 | 12 | tasks.withType(JavaCompile) { 13 | options.compilerArgs << "-Xlint:all" << "-Xlint:-serial" << "-Werror" 14 | } 15 | 16 | dependencies { 17 | testImplementation(platform('org.junit:junit-bom:5.8.2')) 18 | testImplementation('org.junit.jupiter:junit-jupiter') 19 | } 20 | 21 | java { 22 | withJavadocJar() 23 | withSourcesJar() 24 | sourceCompatibility = JavaVersion.VERSION_1_8 25 | targetCompatibility = JavaVersion.VERSION_1_8 26 | } 27 | 28 | group 'com.kirekov' 29 | version 'DEV-SNAPSHOT' 30 | description 'Java utilities to throw checked exceptions in a "sneaky" way' 31 | 32 | pmd { 33 | consoleOutput = true 34 | toolVersion = "6.31.0" 35 | ignoreFailures = false 36 | ruleSets = [] 37 | } 38 | 39 | pmdMain { 40 | ruleSetFiles = files("codestyle/pmd_main.xml") 41 | } 42 | 43 | pmdTest { 44 | ruleSetFiles = files("codestyle/pmd_test.xml") 45 | } 46 | 47 | task runStaticAnalysis(dependsOn: [checkstyleMain, checkstyleTest, pmdMain, pmdTest]) 48 | 49 | checkstyle { 50 | configFile = file('codestyle/checkstyle.xml') 51 | ignoreFailures = false 52 | maxWarnings = 0 53 | showViolations = true 54 | toolVersion = '8.42' 55 | } 56 | 57 | repositories { 58 | mavenCentral() 59 | } 60 | 61 | dependencies { 62 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.1' 63 | testImplementation 'org.mockito:mockito-core:2.28.2' 64 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.5.1' 65 | } 66 | 67 | test { 68 | useJUnitPlatform() 69 | testLogging { 70 | events "passed", "skipped", "failed" 71 | } 72 | } 73 | 74 | task testsJar(type: Jar) { 75 | archiveClassifier = 'tests' 76 | from sourceSets.test.output 77 | } 78 | 79 | publishing { 80 | publications { 81 | sneakyJava(MavenPublication) { 82 | artifact jar { 83 | extension 'jar' 84 | } 85 | artifact javadocJar { 86 | classifier 'javadoc' 87 | extension 'jar' 88 | } 89 | artifact sourcesJar { 90 | classifier 'sources' 91 | extension 'jar' 92 | } 93 | artifact testsJar { 94 | classifier 'tests' 95 | extension 'jar' 96 | } 97 | pom { 98 | name = 'Sneaky Java' 99 | description = 'Java utilities to throw checked exceptions in a "sneaky" way' 100 | url = 'https://github.com/SimonHarmonicMinor/sneaky-java' 101 | licenses { 102 | license { 103 | name = 'MIT License' 104 | url = 'https://www.mit.edu/~amini/LICENSE.md' 105 | } 106 | } 107 | developers { 108 | developer { 109 | id = 'kirekov' 110 | name = 'Semyon Kirekov' 111 | email = 'semyon@kirekov.com' 112 | } 113 | } 114 | scm { 115 | connection = 'scm:https://github.com/SimonHarmonicMinor/sneaky-java.git' 116 | developerConnection = 'scm:git://github.com/SimonHarmonicMinor/sneaky-java.git' 117 | url = 'https://github.com/SimonHarmonicMinor/sneaky-java' 118 | } 119 | } 120 | } 121 | } 122 | repositories { 123 | maven { 124 | def releasesRepoUrl = 'https://oss.sonatype.org/service/local/staging/deploy/maven2/' 125 | def snapshotsRepoUrl = 'https://oss.sonatype.org/content/repositories/snapshots' 126 | url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl 127 | credentials { 128 | username findProperty('ossSonatypeUsername') 129 | password findProperty('ossSonatypePassword') 130 | } 131 | } 132 | } 133 | } 134 | 135 | signing { 136 | sign publishing.publications.sneakyJava 137 | } 138 | 139 | nexusPublishing { 140 | repositories { 141 | sonatype { 142 | username = findProperty('ossSonatypeUsername') 143 | password = findProperty('ossSonatypePassword') 144 | } 145 | } 146 | } 147 | 148 | javadoc { 149 | if (JavaVersion.current().isJava9Compatible()) { 150 | options.addBooleanOption('html5', true) 151 | } 152 | } 153 | 154 | jacocoTestReport { 155 | reports { 156 | xml.enabled true 157 | } 158 | } 159 | 160 | sonarqube { 161 | properties { 162 | property "sonar.projectKey", "SimonHarmonicMinor_sneaky-java" 163 | property "sonar.coverage.jacoco.xmlReportPaths", "build/reports/jacoco/test/jacocoTestReport.xml" 164 | property "sonar.organization", "simonharmonicminor" 165 | property "sonar.host.url", "https://sonarcloud.io" 166 | } 167 | } -------------------------------------------------------------------------------- /codestyle/checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 56 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 74 | 75 | 76 | 78 | 79 | 80 | 86 | 87 | 88 | 89 | 92 | 93 | 94 | 95 | 96 | 100 | 101 | 102 | 103 | 104 | 106 | 107 | 108 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 127 | 129 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 177 | 178 | 179 | 181 | 183 | 184 | 185 | 186 | 188 | 189 | 190 | 191 | 193 | 194 | 195 | 196 | 198 | 199 | 200 | 201 | 203 | 204 | 205 | 206 | 208 | 209 | 210 | 211 | 213 | 214 | 215 | 216 | 218 | 219 | 220 | 221 | 223 | 224 | 225 | 226 | 228 | 229 | 230 | 231 | 233 | 234 | 235 | 236 | 238 | 239 | 240 | 241 | 243 | 245 | 247 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 278 | 279 | 280 | 283 | 284 | 285 | 286 | 292 | 293 | 294 | 295 | 299 | 300 | 301 | 302 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 316 | 317 | 318 | 319 | 320 | 321 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 337 | 338 | 339 | 340 | 343 | 344 | 345 | 346 | 347 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 361 | 362 | 363 | 364 | -------------------------------------------------------------------------------- /codestyle/idea_code_style.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | 21 | 28 | -------------------------------------------------------------------------------- /codestyle/pmd_test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | Rules which enforce generally accepted best practices. 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 46 | 47 | The abstract class does not contain any abstract methods. An abstract class suggests 48 | an incomplete implementation, which is to be completed by subclasses implementing the 49 | abstract methods. If the class is intended to be used as a base class only (not to be 50 | instantiated 51 | directly) a protected constructor can be provided prevent direct instantiation. 52 | 53 | 3 54 | 55 | 63 | 64 | 65 | 66 | 73 | 74 | Instantiation by way of private constructors from outside of the constructor's class often 75 | causes the 76 | generation of an accessor. A factory method, or non-privatization of the constructor can 77 | eliminate this 78 | situation. The generated class file is actually an interface. It gives the accessing class the 79 | ability 80 | to invoke a new hidden package scope constructor that takes the interface as a supplementary 81 | parameter. 82 | This turns a private constructor effectively into one with package scope, and is challenging 83 | to discern. 84 | 85 | 3 86 | 87 | 97 | 98 | 99 | 100 | 107 | 108 | When accessing private fields / methods from another class, the Java compiler will generate 109 | accessor methods 110 | with package-private visibility. This adds overhead, and to the dex method count on Android. 111 | This situation can 112 | be avoided by changing the visibility of the field / method from private to package-private. 113 | 114 | 3 115 | 116 | 130 | 131 | 132 | 133 | 139 | 140 | Constructors and methods receiving arrays should clone objects and store the copy. 141 | This prevents future changes from the user from affecting the original array. 142 | 143 | 3 144 | 145 | 154 | 155 | 156 | 157 | 164 | 165 | Declaring a MessageDigest instance as a field make this instance directly available to 166 | multiple threads. 167 | Such sharing of MessageDigest instances should be avoided if possible since it leads to wrong 168 | results 169 | if the access is not synchronized correctly. 170 | Just create a new instance and use it locally, where you need it. 171 | Creating a new instance is easier than synchronizing access to a shared instance. 172 | 173 | 3 174 | 175 | 176 | 177 | 178 | 181 | 182 | 183 | 184 | 185 | 207 | 208 | 209 | 210 | 216 | 217 | Avoid printStackTrace(); use a logger call instead. 218 | 219 | 3 220 | 221 | 222 | 223 | 224 | 231 | 232 | 233 | 234 | 235 | 246 | 247 | 248 | 249 | 255 | 256 | Reassigning exception variables caught in a catch statement should be avoided because of: 257 | 258 | 1) If it is needed, multi catch can be easily added and code will still compile. 259 | 260 | 2) Following the principle of least surprise we want to make sure that a variable caught in a 261 | catch statement 262 | is always the one thrown in a try block. 263 | 264 | 3 265 | 281 | 282 | 283 | 289 | 290 | Reassigning loop variables can lead to hard-to-find bugs. Prevent or limit how these variables 291 | can be changed. 292 | 293 | In foreach-loops, configured by the `foreachReassign` property: 294 | - `deny`: Report any reassignment of the loop variable in the loop body. _This is the 295 | default._ 296 | - `allow`: Don't check the loop variable. 297 | - `firstOnly`: Report any reassignments of the loop variable, except as the first statement in 298 | the loop body. 299 | _This is useful if some kind of normalization or clean-up of the value before using is 300 | permitted, but any other change of the variable is not._ 301 | 302 | In for-loops, configured by the `forReassign` property: 303 | - `deny`: Report any reassignment of the control variable in the loop body. _This is the 304 | default._ 305 | - `allow`: Don't check the control variable. 306 | - `skip`: Report any reassignments of the control variable, except conditional 307 | increments/decrements (`++`, `--`, `+=`, `-=`). 308 | _This prevents accidental reassignments or unconditional increments of the control variable._ 309 | 310 | 3 311 | 312 | 331 | 332 | 333 | 334 | 340 | 341 | Reassigning values to incoming parameters of a method or constructor is not recommended, as 342 | this can 343 | make the code more difficult to understand. The code is often read with the assumption that 344 | parameter values 345 | don't change and an assignment violates therefore the principle of least astonishment. This is 346 | especially a 347 | problem if the parameter is documented e.g. in the method's javadoc and the new content 348 | differs from the original 349 | documented content. 350 | 351 | Use temporary local variables instead. This allows you to assign a new name, which makes the 352 | code better 353 | understandable. 354 | 355 | Note that this rule considers both methods and constructors. If there are multiple assignments 356 | for a formal 357 | parameter, then only the first assignment is reported. 358 | 359 | 2 360 | 361 | 372 | 373 | 374 | 375 | 381 | 382 | StringBuffers/StringBuilders can grow considerably, and so may become a source of memory leaks 383 | if held within objects with long lifetimes. 384 | 385 | 3 386 | 387 | 388 | 389 | 390 | 393 | 394 | 395 | 396 | 397 | 402 | 403 | 404 | 405 | 411 | 412 | Application with hard-coded IP addresses can become impossible to deploy in some cases. 413 | Externalizing IP adresses is preferable. 414 | 415 | 3 416 | 417 | 422 | 423 | 424 | 425 | 431 | 432 | Always check the return values of navigation methods (next, previous, first, last) of a 433 | ResultSet. 434 | If the value return is 'false', it should be handled properly. 435 | 436 | 3 437 | 438 | 451 | 452 | 453 | 454 | 460 | 461 | Avoid constants in interfaces. Interfaces should define types, constants are implementation 462 | details 463 | better placed in classes or enums. See Effective Java, item 19. 464 | 465 | 3 466 | 467 | 470 | 471 | 472 | 473 | 476 | 477 | 478 | 479 | 480 | 498 | 499 | 500 | 501 | 507 | 508 | By convention, the default label should be the last label in a switch statement. 509 | 510 | 3 511 | 512 | 513 | 514 | 515 | 520 | 521 | 522 | 523 | 524 | 538 | 539 | 540 | 541 | 547 | 548 | Double brace initialisation is a pattern to initialise eg collections concisely. But it 549 | implicitly 550 | generates a new .class file, and the object holds a strong reference to the enclosing object. 551 | For those 552 | reasons, it is preferable to initialize the object normally, even though it's verbose. 553 | 554 | This rule counts any anonymous class which only has a single initializer as an instance of 555 | double-brace 556 | initialization. There is currently no way to find out whether a method called in the 557 | initializer is not 558 | accessible from outside the anonymous class, and those legit cases should be suppressed for 559 | the time being. 560 | 561 | 3 562 | 563 | 564 | 565 | 566 | 569 | 570 | 571 | 572 | (){{ 575 | add("a"); 576 | add("b"); 577 | add("c"); 578 | }}; 579 | // the better way is to not create an anonymous class: 580 | List a = new ArrayList<>(); 581 | a.add("a"); 582 | a.add("b"); 583 | a.add("c"); 584 | return a; 585 | ]]> 586 | 587 | 588 | 589 | 590 | 598 | 599 | Reports loops that can be safely replaced with the foreach syntax. The rule considers loops 600 | over 601 | lists, arrays and iterators. A loop is safe to replace if it only uses the index variable to 602 | access an element of the list or array, only has one update statement, and loops through 603 | *every* 604 | element of the list or array left to right. 605 | 606 | 3 607 | 608 | l) { 611 | for (int i = 0; i < l.size(); i++) { // pre Java 1.5 612 | System.out.println(l.get(i)); 613 | } 614 | for (String s : l) { // post Java 1.5 615 | System.out.println(s); 616 | } 617 | } 618 | } 619 | ]]> 620 | 621 | 622 | 623 | 629 | 630 | Having a lot of control variables in a 'for' loop makes it harder to see what range of values 631 | the loop iterates over. By default this rule allows a regular 'for' loop with only one 632 | variable. 633 | 634 | 3 635 | 636 | 639 | 640 | 641 | //ForInit/LocalVariableDeclaration[count(VariableDeclarator) > $maximumVariables] 642 | 643 | 644 | 645 | 646 | 651 | 652 | 653 | 654 | 660 | 661 | Whenever using a log level, one should check if the loglevel is actually enabled, or 662 | otherwise skip the associate String creation and manipulation. 663 | 664 | An alternative to checking the log level are substituting parameters, formatters or lazy 665 | logging 666 | with lambdas. The available alternatives depend on the actual logging framework. 667 | 668 | 2 669 | 670 | calculateExpensiveLoggingText()); 681 | ]]> 682 | 683 | 684 | 685 | 692 | 693 | In JUnit 3, test suites are indicated by the suite() method. In JUnit 4, suites are indicated 694 | through the @RunWith(Suite.class) annotation. 695 | 696 | 3 697 | 698 | 699 | 700 | 701 | 706 | 707 | 708 | 709 | 710 | 721 | 722 | 723 | 724 | 731 | 732 | In JUnit 3, the tearDown method was used to clean up all data entities required in running 733 | tests. 734 | JUnit 4 skips the tearDown method and executes all methods annotated with @After after running 735 | each test. 736 | JUnit 5 introduced @AfterEach and @AfterAll annotations to execute methods after each test or 737 | after all tests in the class, respectively. 738 | 739 | 3 740 | 741 | 742 | 743 | 744 | 753 | 754 | 755 | 756 | 757 | 769 | 770 | 771 | 772 | 779 | 780 | In JUnit 3, the setUp method was used to set up all data entities required in running tests. 781 | JUnit 4 skips the setUp method and executes all methods annotated with @Before before all 782 | tests. 783 | JUnit 5 introduced @BeforeEach and @BeforeAll annotations to execute methods before each test 784 | or before all tests in the class, respectively. 785 | 786 | 3 787 | 788 | 789 | 790 | 791 | 800 | 801 | 802 | 803 | 804 | 816 | 817 | 818 | 819 | 826 | 827 | In JUnit 3, the framework executed all methods which started with the word test as a unit 828 | test. 829 | In JUnit 4, only methods annotated with the @Test annotation are executed. 830 | In JUnit 5, one of the following annotations should be used for tests: @Test, @RepeatedTest, 831 | @TestFactory, @TestTemplate or @ParameterizedTest. 832 | 833 | 3 834 | 835 | 836 | 837 | 838 | 850 | 851 | 852 | 854 | 855 | 856 | 857 | 868 | 869 | 870 | 871 | 878 | 886 | 3 887 | 888 | 889 | 890 | 891 | 913 | 914 | 915 | 916 | 917 | 929 | 930 | 931 | 932 | 938 | 939 | In JUnit4, use the @Test(expected) annotation to denote tests that should throw exceptions. 940 | 941 | 3 942 | 943 | 959 | 960 | 961 | 962 | 968 | 969 | Position literals first in all String comparisons, if the second argument is null then 970 | NullPointerExceptions 971 | can be avoided, they will just return false. Note that switching literal positions for 972 | compareTo and 973 | compareToIgnoreCase may change the result, see examples. 974 | 975 | 3 976 | 977 | 0); // should be: "bar".compareTo(x) < 0 987 | } 988 | boolean bar(String x) { 989 | return (x.compareToIgnoreCase("bar") > 0); // should be: "bar".compareToIgnoreCase(x) < 0 990 | } 991 | boolean bar(String x) { 992 | return x.contentEquals("bar"); // should be "bar".contentEquals(x) 993 | } 994 | } 995 | ]]> 996 | 997 | 998 | 999 | 1006 | 1007 | The use of implementation types (i.e., HashSet) as object references limits your ability to 1008 | use alternate 1009 | implementations in the future as requirements change. Whenever available, referencing objects 1010 | by their interface types (i.e, Set) provides much more flexibility. 1011 | 1012 | 3 1013 | 1014 | list = new ArrayList<>(); 1020 | public HashSet getFoo() { 1021 | return new HashSet(); 1022 | } 1023 | // preferred approach 1024 | private List list = new ArrayList<>(); 1025 | public Set getFoo() { 1026 | return new HashSet(); 1027 | } 1028 | } 1029 | ]]> 1030 | 1031 | 1032 | 1033 | 1039 | 1040 | Exposing internal arrays to the caller violates object encapsulation since elements can be 1041 | removed or replaced outside of the object that owns it. It is safer to return a copy of the 1042 | array. 1043 | 1044 | 3 1045 | 1046 | 1055 | 1056 | 1057 | 1058 | 1059 | 1067 | 1068 | Annotating overridden methods with @Override ensures at compile time that 1069 | the method really overrides one, which helps refactoring and clarifies intent. 1070 | 1071 | 3 1072 | 1073 | 1080 | 1081 | 1082 | 1083 | 1089 | 1090 | Java allows the use of several variables declaration of the same type on one line. However, it 1091 | can lead to quite messy code. This rule looks for several declarations on the same line. 1092 | 1093 | 4 1094 | 1095 | 1096 | 1097 | 1098 | 1] 1102 | [$strictMode or count(distinct-values(VariableDeclarator/@BeginLine)) != count(VariableDeclarator)] 1103 | | 1104 | //FieldDeclaration 1105 | [count(VariableDeclarator) > 1] 1106 | [$strictMode or count(distinct-values(VariableDeclarator/@BeginLine)) != count(VariableDeclarator)] 1107 | ]]> 1108 | 1109 | 1110 | 1111 | 1113 | 1114 | 1115 | 1123 | 1124 | 1125 | 1126 | 1133 | 1134 | Position literals first in comparisons, if the second argument is null then 1135 | NullPointerExceptions 1136 | can be avoided, they will just return false. 1137 | 1138 | This rule is replaced by the more general rule {% rule "LiteralsFirstInComparisons" %}. 1139 | 1140 | 3 1141 | 1142 | 1149 | 1150 | 1151 | 1152 | 1159 | 1160 | Position literals first in comparisons, if the second argument is null then 1161 | NullPointerExceptions 1162 | can be avoided, they will just return false. 1163 | 1164 | This rule is replaced by the more general rule {% rule "LiteralsFirstInComparisons" %}. 1165 | 1166 | 3 1167 | 1168 | 1175 | 1176 | 1177 | 1178 | 1184 | 1185 | Throwing a new exception from a catch block without passing the original exception into the 1186 | new exception will cause the original stack trace to be lost making it difficult to debug 1187 | effectively. 1188 | 1189 | 3 1190 | 1191 | 1214 | 1215 | 1216 | 1217 | 1223 | 1224 | Consider replacing Enumeration usages with the newer java.util.Iterator 1225 | 1226 | 3 1227 | 1228 | 1229 | 1230 | 1231 | 1234 | 1235 | 1236 | 1237 | 1238 | 1249 | 1250 | 1251 | 1252 | 1258 | 1259 | Consider replacing Hashtable usage with the newer java.util.Map if thread safety is not 1260 | required. 1261 | 1262 | 3 1263 | 1264 | 1265 | 1266 | //Type/ReferenceType/ClassOrInterfaceType[@Image='Hashtable'] 1267 | 1268 | 1269 | 1270 | 1277 | 1278 | 1279 | 1280 | 1286 | 1287 | Consider replacing Vector usages with the newer java.util.ArrayList if expensive thread-safe 1288 | operations are not required. 1289 | 1290 | 3 1291 | 1292 | 1293 | 1294 | //Type/ReferenceType/ClassOrInterfaceType[@Image='Vector'] 1295 | 1296 | 1297 | 1298 | 1305 | 1306 | 1307 | 1308 | 1315 | 1316 | Switch statements should be exhaustive, to make their control flow 1317 | easier to follow. This can be achieved by adding a `default` case, or, 1318 | if the switch is on an enum type, by ensuring there is one switch branch 1319 | for each enum constant. 1320 | 1321 | 3 1322 | 1323 | 1324 | 1325 | 1328 | 1329 | 1330 | 1331 | 1341 | 1342 | 1343 | 1344 | 1350 | 1351 | References to System.(out|err).print are usually intended for debugging purposes and can 1352 | remain in 1353 | the codebase even in production code. By using a logger one can enable/disable this behaviour 1354 | at 1355 | will (and by priority) and avoid clogging the Standard out log. 1356 | 1357 | 2 1358 | 1359 | 1360 | 1361 | 1362 | 1369 | 1370 | 1371 | 1372 | 1373 | 1383 | 1384 | 1385 | 1386 | 1392 | 1393 | Reports assignments to variables that are never used before the variable is overwritten, 1394 | or goes out of scope. Unused assignments are those for which 1395 | 1. The variable is never read after the assignment, or 1396 | 2. The assigned value is always overwritten by other assignments before the next read of 1397 | the variable. 1398 | 1399 | The rule doesn't consider assignments to fields except for those of `this` in a constructor, 1400 | or static fields of the current class in static initializers. 1401 | 1402 | The rule may be suppressed with the standard `@SuppressWarnings("unused")` tag. 1403 | 1404 | The rule subsumes {% rule "UnusedLocalVariable" %}, and {% rule "UnusedFormalParameter" %}. 1405 | Those violations are filtered 1406 | out by default, in case you already have enabled those rules, but may be enabled with the 1407 | property 1408 | `reportUnusedVariables`. Variables whose name starts with `ignored` or `unused` are filtered 1409 | out, as 1410 | is standard practice for exceptions. 1411 | 1412 | Limitations: 1413 | * The rule currently cannot know which method calls throw exceptions, or which exceptions they 1414 | throw. 1415 | In the body of a try block, every method or constructor call is assumed to throw. This may 1416 | cause false-negatives. 1417 | The only other language construct that is assumed to throw is the `throw` statement, in 1418 | particular, 1419 | things like `assert` statements, or NullPointerExceptions on dereference are ignored. 1420 | * The rule cannot resolve assignments across constructors, when they're called with the 1421 | special 1422 | `this(...)` syntax. This may cause false-negatives. 1423 | 1424 | Both of those limitations may be partly relaxed in PMD 7. 1425 | 1426 | 3 1427 | 1428 | 1438 | 1439 | 1456 | 1457 | 1458 | 1474 | 1475 | 1476 | 1496 | 1497 | 1498 | 1499 | 1500 | 1506 | 1507 | Reports parameters of methods and constructors that are not referenced them in the method 1508 | body. 1509 | Parameters whose name starts with `ignored` or `unused` are filtered out. 1510 | 1511 | Removing unused formal parameters from public methods could cause a ripple effect through the 1512 | code base. 1513 | Hence, by default, this rule only considers private methods. To include non-private methods, 1514 | set the 1515 | `checkAll` property to `true`. 1516 | 1517 | 1518 | 3 1519 | 1520 | 1527 | 1528 | 1529 | 1530 | 1538 | 1539 | Reports import statements that are not used within the file. This also reports 1540 | duplicate imports, and imports from the same package. The simplest fix is just 1541 | to delete those imports. 1542 | 1543 | This rule is deprecated since PMD 6.34.0. Use the rule {% rule 1544 | "java/codestyle/UnnecessaryImport" %} 1545 | from category codestyle instead. 1546 | 1547 | 4 1548 | 1549 | 1554 | 1555 | 1556 | 1557 | 1563 | 1564 | Detects when a local variable is declared and/or assigned, but not used. 1565 | Variables whose name starts with `ignored` or `unused` are filtered out. 1566 | 1567 | 3 1568 | 1569 | 1576 | 1577 | 1578 | 1579 | 1585 | 1586 | Detects when a private field is declared and/or assigned a value, but not used. 1587 | 1588 | 3 1589 | 1590 | 1600 | 1601 | 1602 | 1603 | 1609 | 1610 | Unused Private Method detects when a private method is declared but is unused. 1611 | 1612 | 3 1613 | 1614 | 1619 | 1620 | 1621 | 1622 | 1629 | 1630 | This rule detects JUnit assertions in object equality. These assertions should be made by more 1631 | specific methods, like assertEquals. 1632 | 1633 | 3 1634 | 1635 | 1636 | 1637 | 1638 | 1657 | 1658 | 1659 | 1660 | 1661 | 1670 | 1671 | 1672 | 1673 | 1680 | 1681 | This rule detects JUnit assertions in object references equality. These assertions should be 1682 | made by 1683 | more specific methods, like assertNull, assertNotNull. 1684 | 1685 | 3 1686 | 1687 | 1688 | 1689 | 1690 | 1710 | 1711 | 1712 | 1713 | 1714 | 1725 | 1726 | 1727 | 1728 | 1735 | 1736 | This rule detects JUnit assertions in object references equality. These assertions should be 1737 | made 1738 | by more specific methods, like assertSame, assertNotSame. 1739 | 1740 | 3 1741 | 1742 | 1743 | 1744 | 1745 | 1765 | 1766 | 1767 | 1768 | 1769 | 1778 | 1779 | 1780 | 1781 | 1787 | 1788 | When asserting a value is the same as a literal or Boxed boolean, use assertTrue/assertFalse, 1789 | instead of assertEquals. 1790 | 1791 | 3 1792 | 1793 | 1794 | 1795 | 1796 | 1805 | 1806 | 1807 | 1808 | 1809 | 1826 | 1827 | 1828 | 1829 | 1835 | 1836 | The isEmpty() method on java.util.Collection is provided to determine if a collection has any 1837 | elements. 1838 | Comparing the value of size() to 0 does not convey intent as well as the isEmpty() method. 1839 | 1840 | 3 1841 | 1842 | 1858 | 1859 | 1860 | 1861 | 1868 | 1869 | Starting with Java 7, StandardCharsets provides constants for common Charset objects, such as 1870 | UTF-8. 1871 | Using the constants is less error prone, and can provide a small performance advantage 1872 | compared to `Charset.forName(...)` 1873 | since no scan across the internal `Charset` caches is needed. 1874 | 1875 | 3 1876 | 1877 | 1878 | 1879 | 1880 | 1885 | 1886 | 1887 | 1888 | 1889 | 1903 | 1904 | 1905 | 1906 | 1914 | 1915 | Java 7 introduced the try-with-resources statement. This statement ensures that each resource 1916 | is closed at the end 1917 | of the statement. It avoids the need of explicitly closing the resources in a finally block. 1918 | Additionally exceptions 1919 | are better handled: If an exception occurred both in the `try` block and `finally` block, then 1920 | the exception from 1921 | the try block was suppressed. With the `try`-with-resources statement, the exception thrown 1922 | from the try-block is 1923 | preserved. 1924 | 1925 | 3 1926 | 1927 | 1930 | 1931 | 1932 | 1933 | 1942 | 1943 | 1944 | 1945 | 1946 | 1969 | 1970 | 1971 | 1972 | 1980 | 1981 | Java 5 introduced the varargs parameter declaration for methods and constructors. This 1982 | syntactic 1983 | sugar provides flexibility for users of these methods and constructors, allowing them to avoid 1984 | having to deal with the creation of an array. 1985 | 1986 | Byte arrays in any method and String arrays in `public static void main(String[])` methods are 1987 | ignored. 1988 | 1989 | 4 1990 | 1991 | 1992 | 1993 | 1994 | 2015 | 2016 | 2017 | 2018 | 2019 | 2029 | 2030 | 2031 | 2032 | 2038 | 2039 | `do {} while (true);` requires reading the end of the statement before it is 2040 | apparent that it loops forever, whereas `while (true) {}` is easier to understand. 2041 | 2042 | `do {} while (false);` is redundant, and if an inner variable scope is required, 2043 | a block `{}` is sufficient. 2044 | 2045 | `while (false) {}` will never execute the block and can be removed in its entirety. 2046 | 2047 | 3 2048 | 2049 | 2050 | 2051 | //DoStatement[Expression/PrimaryExpression/PrimaryPrefix/Literal/BooleanLiteral] | 2052 | //WhileStatement[Expression/PrimaryExpression/PrimaryPrefix/Literal/BooleanLiteral[@True = 2053 | false()]] 2054 | 2055 | 2056 | 2057 | 2058 | 2059 | public class Example { 2060 | { 2061 | while (true) { } // allowed 2062 | while (false) { } // disallowed 2063 | do { } while (true); // disallowed 2064 | do { } while (false); // disallowed 2065 | } 2066 | } 2067 | 2068 | 2069 | 2070 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimonHarmonicMinor/sneaky-java/b71c075d2864584d96010798b42c7b2de2e4385b/gradle.properties -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimonHarmonicMinor/sneaky-java/b71c075d2864584d96010798b42c7b2de2e4385b/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'sneaky-java' -------------------------------------------------------------------------------- /src/main/java/com/kirekov/sneaky/Sneaky.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.sneaky; 2 | 3 | import com.kirekov.sneaky.lambda.CheckedBiConsumer; 4 | import com.kirekov.sneaky.lambda.CheckedBiFunction; 5 | import com.kirekov.sneaky.lambda.CheckedBiPredicate; 6 | import com.kirekov.sneaky.lambda.CheckedConsumer; 7 | import com.kirekov.sneaky.lambda.CheckedFunction; 8 | import com.kirekov.sneaky.lambda.CheckedPredicate; 9 | import com.kirekov.sneaky.lambda.CheckedSupplier; 10 | import java.util.function.BiConsumer; 11 | import java.util.function.BiFunction; 12 | import java.util.function.BiPredicate; 13 | import java.util.function.Consumer; 14 | import java.util.function.Function; 15 | import java.util.function.Predicate; 16 | import java.util.function.Supplier; 17 | 18 | /** 19 | * Factory for sneaky wrappers. 20 | */ 21 | public final class Sneaky { 22 | 23 | private Sneaky() { 24 | // no op 25 | } 26 | 27 | /** 28 | * Returns {@linkplain Consumer} that may throw {@linkplain Exception} ignoring {@code throws 29 | * Exception} clause in the method signature. 30 | * 31 | * @param checkedConsumer consumer that throws {@linkplain Exception} 32 | * @param the input argument 33 | * @return wrapped consumer 34 | */ 35 | public static Consumer consumer(CheckedConsumer checkedConsumer) { 36 | return value -> { 37 | try { 38 | checkedConsumer.accept(value); 39 | } catch (Exception e) { 40 | throwUnchecked(e); 41 | } 42 | }; 43 | } 44 | 45 | /** 46 | * Returns {@linkplain BiConsumer} that may throw {@linkplain Exception} ignoring {@code throws 47 | * Exception} clause in the method signature. 48 | * 49 | * @param checkedBiConsumer biConsumer that throws {@linkplain Exception} 50 | * @param the first input argument 51 | * @param the second input argument 52 | * @return wrapped biConsumer 53 | */ 54 | public static BiConsumer biConsumer(CheckedBiConsumer checkedBiConsumer) { 55 | return (t, u) -> { 56 | try { 57 | checkedBiConsumer.accept(t, u); 58 | } catch (Exception e) { 59 | throwUnchecked(e); 60 | } 61 | }; 62 | } 63 | 64 | /** 65 | * Returns {@linkplain Predicate} that may throw {@linkplain Exception} ignoring {@code throws 66 | * Exception} clause in the method signature. 67 | * 68 | * @param checkedPredicate predicate that throws {@linkplain Exception} 69 | * @param the input argument 70 | * @return wrapped predicate 71 | */ 72 | public static Predicate predicate(CheckedPredicate checkedPredicate) { 73 | return t -> { 74 | try { 75 | return checkedPredicate.test(t); 76 | } catch (Exception e) { 77 | throwUnchecked(e); 78 | return false; 79 | } 80 | }; 81 | } 82 | 83 | /** 84 | * Returns {@linkplain BiPredicate} that may throw {@linkplain Exception} ignoring {@code throws 85 | * Exception} clause in the method signature. 86 | * 87 | * @param checkedBiPredicate biPredicate that throws {@linkplain Exception} 88 | * @param the first input argument 89 | * @param the second input argument 90 | * @return wrapped biPredicate 91 | */ 92 | public static BiPredicate biPredicate(CheckedBiPredicate checkedBiPredicate) { 93 | return (t, u) -> { 94 | try { 95 | return checkedBiPredicate.test(t, u); 96 | } catch (Exception e) { 97 | throwUnchecked(e); 98 | return false; 99 | } 100 | }; 101 | } 102 | 103 | /** 104 | * Returns {@linkplain Supplier} that may throw {@linkplain Exception} ignoring {@code throws 105 | * Exception} clause in the method signature. 106 | * 107 | * @param checkedSupplier supplier that throws {@linkplain Exception} 108 | * @param the result of supplier execution 109 | * @return wrapped supplier 110 | */ 111 | public static Supplier supplier(CheckedSupplier checkedSupplier) { 112 | return () -> { 113 | try { 114 | return checkedSupplier.get(); 115 | } catch (Exception e) { 116 | throwUnchecked(e); 117 | return null; 118 | } 119 | }; 120 | } 121 | 122 | /** 123 | * Returns {@linkplain Function} that may throw {@linkplain Exception} ignoring {@code throws 124 | * Exception} clause in the method signature. 125 | * 126 | * @param checkedFunction function that throws {@linkplain Exception} 127 | * @param the function argument 128 | * @param the function result 129 | * @return wrapped function 130 | */ 131 | public static Function function(CheckedFunction checkedFunction) { 132 | return t -> { 133 | try { 134 | return checkedFunction.apply(t); 135 | } catch (Exception e) { 136 | throwUnchecked(e); 137 | return null; 138 | } 139 | }; 140 | } 141 | 142 | /** 143 | * Returns {@linkplain BiFunction} that may throw {@linkplain Exception} ignoring {@code throws 144 | * Exception} clause in the method signature. 145 | * 146 | * @param checkedBiFunction biFunction that throws {@linkplain Exception} 147 | * @param the first function argument 148 | * @param the second function argument 149 | * @param the function result 150 | * @return wrapped function 151 | */ 152 | public static BiFunction biFunction( 153 | CheckedBiFunction checkedBiFunction 154 | ) { 155 | return (t, u) -> { 156 | try { 157 | return checkedBiFunction.apply(t, u); 158 | } catch (Exception e) { 159 | throwUnchecked(e); 160 | return null; 161 | } 162 | }; 163 | } 164 | 165 | /** 166 | * Throws {@linkplain Exception}. Does not require adding {@code throws Exception} to a method 167 | * signature. 168 | * 169 | * @param exception exception to throw 170 | * @param the type of the exception to throw 171 | * @throws T exception throw. Due to Java generic erasure the actual type is always {@linkplain 172 | * Exception} 173 | */ 174 | @SuppressWarnings("unchecked") 175 | public static void throwUnchecked(Exception exception) throws T { 176 | throw (T) exception; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/sneaky/lambda/CheckedBiConsumer.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.sneaky.lambda; 2 | 3 | import java.util.function.BiConsumer; 4 | 5 | /** 6 | * BiConsumer that may throw {@linkplain Exception}. 7 | * 8 | * @param the first input argument 9 | * @param the second input argument 10 | * @see BiConsumer 11 | */ 12 | @FunctionalInterface 13 | public interface CheckedBiConsumer { 14 | 15 | /** 16 | * Performs the operation on the given arguments. 17 | * 18 | * @param t the first input argument 19 | * @param u the second input argument 20 | * @throws Exception if any error occurs 21 | */ 22 | void accept(T t, U u) throws Exception; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/sneaky/lambda/CheckedBiFunction.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.sneaky.lambda; 2 | 3 | import java.util.function.BiFunction; 4 | 5 | /** 6 | * BiFunction that may throw {@linkplain Exception}. 7 | * 8 | * @param the type of the first argument to the function 9 | * @param the type of the second argument to the function 10 | * @param the type of the result of the function 11 | * @see BiFunction 12 | */ 13 | public interface CheckedBiFunction { 14 | 15 | /** 16 | * Applies this function to the given arguments. 17 | * 18 | * @param t the first function argument 19 | * @param u the second function argument 20 | * @return the function result 21 | * @throws Exception if any error occurs 22 | */ 23 | R apply(T t, U u) throws Exception; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/sneaky/lambda/CheckedBiPredicate.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.sneaky.lambda; 2 | 3 | import java.util.function.BiPredicate; 4 | 5 | /** 6 | * BiPredicate that may throw {@linkplain Exception}. 7 | * 8 | * @param the first input argument 9 | * @param the second input argument 10 | * @see BiPredicate 11 | */ 12 | @FunctionalInterface 13 | public interface CheckedBiPredicate { 14 | 15 | /** 16 | * Evaluates this predicate on the given arguments. 17 | * 18 | * @param t the first input argument 19 | * @param u the second input argument 20 | * @return true if the input arguments match the predicate, otherwise false 21 | * @throws Exception if any error occurs 22 | */ 23 | boolean test(T t, U u) throws Exception; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/sneaky/lambda/CheckedConsumer.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.sneaky.lambda; 2 | 3 | import java.util.function.Consumer; 4 | 5 | /** 6 | * Consumer that may throw {@linkplain Exception}. 7 | * 8 | * @param the type of the input parameter 9 | * @see Consumer 10 | */ 11 | @FunctionalInterface 12 | public interface CheckedConsumer { 13 | 14 | /** 15 | * Performs this operation on the given argument. 16 | * 17 | * @param t the input argument 18 | * @throws Exception if any error occurs 19 | */ 20 | void accept(T t) throws Exception; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/sneaky/lambda/CheckedFunction.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.sneaky.lambda; 2 | 3 | import java.util.function.Function; 4 | 5 | /** 6 | * Function that may throw {@linkplain Exception}. 7 | * 8 | * @param the type of the input to the function 9 | * @param the type of the result of the function 10 | * @see Function 11 | */ 12 | public interface CheckedFunction { 13 | 14 | /** 15 | * Applies this function to the given argument. 16 | * 17 | * @param t the function argument 18 | * @return the function result 19 | * @throws Exception if any error occurs 20 | */ 21 | R apply(T t) throws Exception; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/sneaky/lambda/CheckedPredicate.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.sneaky.lambda; 2 | 3 | import java.util.function.Predicate; 4 | 5 | /** 6 | * Predicate that may throw {@linkplain Exception}. 7 | * 8 | * @param the input parameter 9 | * @see Predicate 10 | */ 11 | @FunctionalInterface 12 | public interface CheckedPredicate { 13 | 14 | /** 15 | * Evaluates this predicate on the given argument. 16 | * 17 | * @param t the input parameter 18 | * @return true if the input argument matches the predicate, otherwise false 19 | * @throws Exception if any error occurs 20 | */ 21 | boolean test(T t) throws Exception; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/kirekov/sneaky/lambda/CheckedSupplier.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.sneaky.lambda; 2 | 3 | import java.util.function.Supplier; 4 | 5 | /** 6 | * Supplier that may throw {@linkplain Exception}. 7 | * 8 | * @param the type of results supplied by this supplier 9 | * @see Supplier 10 | */ 11 | public interface CheckedSupplier { 12 | 13 | /** 14 | * Gets a result. 15 | * 16 | * @return a result 17 | * @throws Exception if any exception occurs 18 | */ 19 | T get() throws Exception; 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/com/kirekov/sneaky/SneakyTest.java: -------------------------------------------------------------------------------- 1 | package com.kirekov.sneaky; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 4 | import static org.junit.jupiter.api.Assertions.assertEquals; 5 | import static org.junit.jupiter.api.Assertions.assertFalse; 6 | import static org.junit.jupiter.api.Assertions.assertThrows; 7 | import static org.junit.jupiter.api.Assertions.assertTrue; 8 | import static org.junit.jupiter.params.provider.Arguments.arguments; 9 | 10 | import java.io.IOException; 11 | import java.util.function.BiConsumer; 12 | import java.util.function.BiFunction; 13 | import java.util.function.BiPredicate; 14 | import java.util.function.Consumer; 15 | import java.util.function.Function; 16 | import java.util.function.Predicate; 17 | import java.util.function.Supplier; 18 | import java.util.stream.Stream; 19 | import org.junit.jupiter.api.Test; 20 | import org.junit.jupiter.params.ParameterizedTest; 21 | import org.junit.jupiter.params.provider.Arguments; 22 | import org.junit.jupiter.params.provider.MethodSource; 23 | import org.junit.jupiter.params.provider.ValueSource; 24 | 25 | @SuppressWarnings("PMD.UnusedPrivateMethod") 26 | class SneakyTest { 27 | 28 | @ParameterizedTest 29 | @ValueSource(strings = { 30 | "s1", "s2", "s3" 31 | }) 32 | void shouldThrowExceptionForConsumer(String value) { 33 | Consumer consumer = Sneaky.consumer(v -> { 34 | if (v.equals(value)) { 35 | throw new IOException(); 36 | } 37 | }); 38 | 39 | assertThrows( 40 | IOException.class, 41 | () -> consumer.accept(value), 42 | "Should throw IOException" 43 | ); 44 | } 45 | 46 | @ParameterizedTest 47 | @ValueSource(ints = { 48 | 1, 5, 124, -12, 623 49 | }) 50 | void shouldSucceedForConsumer(int value) { 51 | Consumer consumer = Sneaky.consumer(v -> { 52 | if (!v.equals(value)) { 53 | throw new Exception(); 54 | } 55 | }); 56 | 57 | assertDoesNotThrow( 58 | () -> consumer.accept(value), 59 | "Should not throw any exceptions" 60 | ); 61 | } 62 | 63 | @ParameterizedTest 64 | @MethodSource("biConsumerLongs") 65 | void shouldThrowExceptionForBiConsumer(long value1, long value2) { 66 | BiConsumer consumer = Sneaky.biConsumer((v, u) -> { 67 | if (v.equals(value1) && u.equals(value2)) { 68 | throw new Exception(); 69 | } 70 | }); 71 | 72 | assertThrows( 73 | Exception.class, 74 | () -> consumer.accept(value1, value2), 75 | "Should throw Exception" 76 | ); 77 | } 78 | 79 | @ParameterizedTest 80 | @MethodSource("biConsumerLongs") 81 | void shouldSucceedForBiConsumer(long value1, long value2) { 82 | BiConsumer consumer = Sneaky.biConsumer((v, u) -> { 83 | if (!v.equals(value1) || !u.equals(value2)) { 84 | throw new Exception(); 85 | } 86 | }); 87 | 88 | assertDoesNotThrow( 89 | () -> consumer.accept(value1, value2), 90 | "Should not throw Exception" 91 | ); 92 | } 93 | 94 | private static Stream biConsumerLongs() { 95 | return Stream.of( 96 | arguments(1L, 5L), 97 | arguments(-61L, -4315L), 98 | arguments(753L, -48345L), 99 | arguments(64L, 31L) 100 | ); 101 | } 102 | 103 | @ParameterizedTest 104 | @ValueSource(ints = {1, 2, 3, 4, 5}) 105 | void shouldThrowExceptionForPredicate(int value) { 106 | Predicate predicate = Sneaky.predicate(v -> { 107 | if (v.equals(value)) { 108 | throw new Exception(); 109 | } 110 | return true; 111 | }); 112 | 113 | assertThrows( 114 | Exception.class, 115 | () -> predicate.test(value), 116 | "Should throw exception" 117 | ); 118 | } 119 | 120 | @ParameterizedTest 121 | @ValueSource(ints = {1, 2, 3, 4, 5}) 122 | void shouldSucceedForPredicate(int value) { 123 | Predicate predicate = Sneaky.predicate(v -> { 124 | if (!v.equals(value)) { 125 | throw new Exception(); 126 | } 127 | return true; 128 | }); 129 | 130 | boolean result = assertDoesNotThrow( 131 | () -> predicate.test(value), 132 | "Should succeed" 133 | ); 134 | assertTrue(result, "Unexpected predicate result"); 135 | } 136 | 137 | @ParameterizedTest 138 | @MethodSource("biPredicateIntegers") 139 | void shouldThrowExceptionForBiPredicate(int value1, int value2) { 140 | BiPredicate biPredicate = Sneaky.biPredicate((v1, v2) -> { 141 | if (v1.equals(value1) && v2.equals(value2)) { 142 | throw new Exception(); 143 | } 144 | return false; 145 | }); 146 | 147 | assertThrows( 148 | Exception.class, 149 | () -> biPredicate.test(value1, value2), 150 | "Should throw exception" 151 | ); 152 | } 153 | 154 | @ParameterizedTest 155 | @MethodSource("biPredicateIntegers") 156 | void shouldSucceedForBiPredicate(int value1, int value2) { 157 | BiPredicate biPredicate = Sneaky.biPredicate((v1, v2) -> { 158 | if (!v1.equals(value1) || !v2.equals(value2)) { 159 | throw new Exception(); 160 | } 161 | return false; 162 | }); 163 | 164 | boolean result = assertDoesNotThrow( 165 | () -> biPredicate.test(value1, value2), 166 | "Should succeed" 167 | ); 168 | assertFalse(result, "Unexpected biPredicate result"); 169 | } 170 | 171 | private static Stream biPredicateIntegers() { 172 | return Stream.of( 173 | arguments(12, 41), 174 | arguments(-41, 9712), 175 | arguments(5641, 752), 176 | arguments(-5131, 8254) 177 | ); 178 | } 179 | 180 | @Test 181 | void shouldThrowExceptionForSupplier() { 182 | Supplier supplier = Sneaky.supplier(() -> { 183 | throw new Exception(); 184 | }); 185 | 186 | assertThrows( 187 | Exception.class, 188 | supplier::get, 189 | "Should throw exception" 190 | ); 191 | } 192 | 193 | @Test 194 | void shouldSucceedForSupplier() { 195 | Supplier supplier = Sneaky.supplier(() -> "some_string"); 196 | 197 | String result = assertDoesNotThrow(supplier::get, "Should not throw exception"); 198 | assertEquals("some_string", result, "Unexpected supplier result"); 199 | } 200 | 201 | @ParameterizedTest 202 | @MethodSource("functionIntegers") 203 | void shouldThrowExceptionForFunction(int argument, int result) { 204 | Function function = Sneaky.function(t -> { 205 | if (t.equals(argument)) { 206 | throw new Exception(); 207 | } 208 | return result; 209 | }); 210 | 211 | assertThrows( 212 | Exception.class, 213 | () -> function.apply(argument), 214 | "Should throw exception" 215 | ); 216 | } 217 | 218 | @ParameterizedTest 219 | @MethodSource("functionIntegers") 220 | void shouldSucceedForFunction(int argument, int result) { 221 | Function function = Sneaky.function(t -> { 222 | if (!t.equals(argument)) { 223 | throw new Exception(); 224 | } 225 | return result; 226 | }); 227 | 228 | int res = assertDoesNotThrow( 229 | () -> function.apply(argument), 230 | "Should not throw exception" 231 | ); 232 | assertEquals(result, res, "Unexpected function result"); 233 | } 234 | 235 | private static Stream functionIntegers() { 236 | return Stream.of( 237 | arguments(1, 2), 238 | arguments(4, -123), 239 | arguments(81, 4124) 240 | ); 241 | } 242 | 243 | @ParameterizedTest 244 | @MethodSource("biFunctionIntegers") 245 | void shouldThrowExceptionForBiFunction(int arg1, int arg2, int result) { 246 | BiFunction biFunction = Sneaky.biFunction( 247 | (t, u) -> { 248 | if (t.equals(arg1) && u.equals(arg2)) { 249 | throw new Exception(); 250 | } 251 | return result; 252 | } 253 | ); 254 | 255 | assertThrows( 256 | Exception.class, 257 | () -> biFunction.apply(arg1, arg2), 258 | "Should throw exception" 259 | ); 260 | } 261 | 262 | @ParameterizedTest 263 | @MethodSource("biFunctionIntegers") 264 | void shouldSucceedForBiFunction(int arg1, int arg2, int result) { 265 | BiFunction biFunction = Sneaky.biFunction( 266 | (t, u) -> { 267 | if (!t.equals(arg1) || !u.equals(arg2)) { 268 | throw new Exception(); 269 | } 270 | return result; 271 | } 272 | ); 273 | 274 | int res = assertDoesNotThrow( 275 | () -> biFunction.apply(arg1, arg2), 276 | "Should not throw exception" 277 | ); 278 | 279 | assertEquals(result, res, "Unexpected function result"); 280 | } 281 | 282 | private static Stream biFunctionIntegers() { 283 | return Stream.of( 284 | arguments(1, 2, 3), 285 | arguments(-51, 982, 0), 286 | arguments(987, 713, -16851) 287 | ); 288 | } 289 | } --------------------------------------------------------------------------------