From 46e446683a4fc56ac5e8fe5b832a848a8fe35005 Mon Sep 17 00:00:00 2001 From: Bruno Matos Torrao Date: Sun, 25 Feb 2018 14:58:33 -0300 Subject: [PATCH 1/7] add: parsing json --- core-groovy/build.gradle | 13 +++ .../groovy/com/baeldung/json/Account.groovy | 7 ++ .../com/baeldung/json/JsonParser.groovy | 36 +++++++++ .../com/baeldung/json/JsonParserTest.groovy | 80 +++++++++++++++++++ 4 files changed, 136 insertions(+) create mode 100644 core-groovy/build.gradle create mode 100644 core-groovy/src/main/groovy/com/baeldung/json/Account.groovy create mode 100644 core-groovy/src/main/groovy/com/baeldung/json/JsonParser.groovy create mode 100644 core-groovy/src/test/groovy/com/baeldung/json/JsonParserTest.groovy diff --git a/core-groovy/build.gradle b/core-groovy/build.gradle new file mode 100644 index 0000000000..b3f33836da --- /dev/null +++ b/core-groovy/build.gradle @@ -0,0 +1,13 @@ +group 'com.baeldung' +version '1.0-SNAPSHOT' + +apply plugin: 'groovy' + +repositories { + mavenCentral() +} + +dependencies { + compile 'org.codehaus.groovy:groovy-all:2.5.0-alpha-1' + testCompile 'org.spockframework:spock-core:1.1-groovy-2.4' +} diff --git a/core-groovy/src/main/groovy/com/baeldung/json/Account.groovy b/core-groovy/src/main/groovy/com/baeldung/json/Account.groovy new file mode 100644 index 0000000000..84b294f0bd --- /dev/null +++ b/core-groovy/src/main/groovy/com/baeldung/json/Account.groovy @@ -0,0 +1,7 @@ +package com.baeldung.json + +class Account { + String id + BigDecimal value + Date createdAt +} \ No newline at end of file diff --git a/core-groovy/src/main/groovy/com/baeldung/json/JsonParser.groovy b/core-groovy/src/main/groovy/com/baeldung/json/JsonParser.groovy new file mode 100644 index 0000000000..0d7c451972 --- /dev/null +++ b/core-groovy/src/main/groovy/com/baeldung/json/JsonParser.groovy @@ -0,0 +1,36 @@ +package com.baeldung.json + +import groovy.json.JsonGenerator +import groovy.json.JsonOutput +import groovy.json.JsonParserType +import groovy.json.JsonSlurper + +class JsonParser { + + Account toObject(String json) { + JsonSlurper jsonSlurper = new JsonSlurper() + jsonSlurper.parseText(json) as Account + } + + Account toObjectWithIndexOverlay(String json) { + JsonSlurper jsonSlurper = new JsonSlurper(type: JsonParserType.INDEX_OVERLAY) + jsonSlurper.parseText(json) as Account + } + + String toJson(Account account) { + JsonOutput.toJson(account) + } + + String toJson(Account account, String dateFormat, String... fieldsToExclude) { + JsonGenerator generator = new JsonGenerator.Options() + .dateFormat(dateFormat) + .excludeFieldsByName(fieldsToExclude) + .build() + generator.toJson(account) + } + + String prettyfy(String json) { + JsonOutput.prettyPrint(json) + } + +} diff --git a/core-groovy/src/test/groovy/com/baeldung/json/JsonParserTest.groovy b/core-groovy/src/test/groovy/com/baeldung/json/JsonParserTest.groovy new file mode 100644 index 0000000000..2bf2b0be7c --- /dev/null +++ b/core-groovy/src/test/groovy/com/baeldung/json/JsonParserTest.groovy @@ -0,0 +1,80 @@ +package com.baeldung.json + +import spock.lang.Specification + +import java.text.SimpleDateFormat + +class JsonParserTest extends Specification { + + JsonParser jsonParser + + void setup () { + jsonParser = new JsonParser() + } + + def 'Should parse to Account given Json String' () { + given: + def json = '{"id":"1234","value":15.6}' + when: + def account = jsonParser.toObject(json) + then: + account + account instanceof Account + account.id == '1234' + account.value == 15.6 + } + + def 'Should parse to Account given Json String with date property' () { + given: + def json = '{"id":"1234","value":15.6,"createdAt":"2018-01-01T02:00:00+0000"}' + when: + def account = jsonParser.toObjectWithIndexOverlay(json) + then: + account + account instanceof Account + account.id == '1234' + account.value == 15.6 + println account.createdAt + account.createdAt == Date.parse('yyyy-MM-dd', '2018-01-01') + } + + def 'Should parse to Json given an Account object' () { + given: + Account account = new Account( + id: '123', + value: 15.6, + createdAt: new SimpleDateFormat('MM/dd/yyyy').parse('01/01/2018') + ) + when: + def json = jsonParser.toJson(account) + then: + json + json == '{"value":15.6,"createdAt":"2018-01-01T02:00:00+0000","id":"123"}' + } + + def 'Should parse to Json given an Account object, a date format and fields to exclude' () { + given: + Account account = new Account( + id: '123', + value: 15.6, + createdAt: new SimpleDateFormat('MM/dd/yyyy').parse('01/01/2018') + ) + when: + def json = jsonParser.toJson(account, 'MM/dd/yyyy', 'value') + then: + json + json == '{"createdAt":"01/01/2018","id":"123"}' + } + + def 'Should prettify given a json string' () { + given: + String json = '{"value":15.6,"createdAt":"01/01/2018","id":"123456"}' + when: + def jsonPretty = jsonParser.prettyfy(json) + then: + jsonPretty + jsonPretty == '{\n "value": 15.6,\n "createdAt": "01/01/2018",\n "id": "123456"\n}' + } + + +} From 5a0932dff54b4dab3fa77b6743eafebc0cab0683 Mon Sep 17 00:00:00 2001 From: Bruno Matos Torrao Date: Sun, 25 Feb 2018 15:47:51 -0300 Subject: [PATCH 2/7] fix: maven build and tests --- core-groovy/pom.xml | 130 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 core-groovy/pom.xml diff --git a/core-groovy/pom.xml b/core-groovy/pom.xml new file mode 100644 index 0000000000..7ab91c7af2 --- /dev/null +++ b/core-groovy/pom.xml @@ -0,0 +1,130 @@ + + + 4.0.0 + + core-groovy + 1.0-SNAPSHOT + jar + + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + + + + + central + http://jcenter.bintray.com + + + + + + org.codehaus.groovy + groovy + 2.5.0-alpha-1 + + + org.codehaus.groovy + groovy-all + 2.5.0-alpha-1 + + + org.codehaus.groovy + groovy-sql + 2.5.0-alpha-1 + + + org.junit.jupiter + junit-jupiter-engine + ${junit.jupiter.version} + test + + + org.junit.platform + junit-platform-runner + ${junit.platform.version} + test + + + org.hsqldb + hsqldb + 2.4.0 + test + + + + org.spockframework + spock-core + 1.1-groovy-2.4 + test + + + + + + + org.codehaus.gmavenplus + gmavenplus-plugin + 1.6 + + + + addSources + addTestSources + compile + compileTests + + + + + + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + maven-failsafe-plugin + 2.19.1 + + + org.junit.platform + junit-platform-surefire-provider + ${junit.platform.version} + + + + + junit5 + + integration-test + verify + + + + **/*Test5.java + + + + + + + + + + UTF-8 + 1.1.2 + 1.1.2 + 1.1.2 + 1.1.2 + 0.15 + 1.5.0 + + 5.0.0 + 1.0.0 + 4.12.0 + 4.12 + + + \ No newline at end of file From 821360efbadb3c751a918a0f6990cfcee17af2fb Mon Sep 17 00:00:00 2001 From: Bruno Matos Torrao Date: Sun, 25 Feb 2018 16:31:53 -0300 Subject: [PATCH 3/7] fix: assert date --- .../src/test/groovy/com/baeldung/json/JsonParserTest.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-groovy/src/test/groovy/com/baeldung/json/JsonParserTest.groovy b/core-groovy/src/test/groovy/com/baeldung/json/JsonParserTest.groovy index 2bf2b0be7c..c383a1b6da 100644 --- a/core-groovy/src/test/groovy/com/baeldung/json/JsonParserTest.groovy +++ b/core-groovy/src/test/groovy/com/baeldung/json/JsonParserTest.groovy @@ -26,7 +26,7 @@ class JsonParserTest extends Specification { def 'Should parse to Account given Json String with date property' () { given: - def json = '{"id":"1234","value":15.6,"createdAt":"2018-01-01T02:00:00+0000"}' + def json = '{"id":"1234","value":15.6,"createdAt":"2018-01-01T00:00:00+0000"}' when: def account = jsonParser.toObjectWithIndexOverlay(json) then: From a25497f258f65162f6efbdff8bae7e3f427a4bef Mon Sep 17 00:00:00 2001 From: Bruno Matos Torrao Date: Sun, 25 Feb 2018 16:34:58 -0300 Subject: [PATCH 4/7] fix: assert date --- .../src/test/groovy/com/baeldung/json/JsonParserTest.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-groovy/src/test/groovy/com/baeldung/json/JsonParserTest.groovy b/core-groovy/src/test/groovy/com/baeldung/json/JsonParserTest.groovy index c383a1b6da..fcd51d58bc 100644 --- a/core-groovy/src/test/groovy/com/baeldung/json/JsonParserTest.groovy +++ b/core-groovy/src/test/groovy/com/baeldung/json/JsonParserTest.groovy @@ -49,7 +49,7 @@ class JsonParserTest extends Specification { def json = jsonParser.toJson(account) then: json - json == '{"value":15.6,"createdAt":"2018-01-01T02:00:00+0000","id":"123"}' + json == '{"value":15.6,"createdAt":"2018-01-01T00:00:00+0000","id":"123"}' } def 'Should parse to Json given an Account object, a date format and fields to exclude' () { From 30285c1c186b526345239cace0ddeed9a2b2dea4 Mon Sep 17 00:00:00 2001 From: KevinGilmore Date: Mon, 26 Feb 2018 19:39:11 -0600 Subject: [PATCH 5/7] BAEL-1507 README (#3735) * BAEL-973: updated README * BAEL-1069: Updated README * BAEL-817: add README file * BAEL-1084: README update * BAEL-960: Update README * BAEL-1155: updated README * BAEL-1041: updated README * BAEL-973: Updated README * BAEL-1187: updated README * BAEL-1183: Update README * BAEL-1133: Updated README * BAEL-1098: README update * BAEL-719: add README.md * BAEL-1272: README update * BAEL-1272: README update * BAEL-1196: Update README * BAEL-1328: Updated README * BAEL-1371: Update README.md * BAEL-1371: Update README.md * BAEL-1278: Update README * BAEL-1326: Update README * BAEL-399: Update README * BAEL-1297: Update README * BAEL-1218: README * BAEL-1148 README update * BAEL-113 README * BAEL-1158 README * BAEL-1539: Update README * BAEL-1507 README update --- core-java/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/core-java/README.md b/core-java/README.md index b160d2271e..5be4e12592 100644 --- a/core-java/README.md +++ b/core-java/README.md @@ -128,3 +128,4 @@ - [Recursion In Java](http://www.baeldung.com/java-recursion) - [A Guide to the finalize Method in Java](http://www.baeldung.com/java-finalize) - [Compiling Java *.class Files with javac](http://www.baeldung.com/javac) +- [Method Overloading and Overriding in Java](http://www.baeldung.com/java-method-overload-override) From ef1b033b7846bd9071f1ede9a0c57996f24d32c5 Mon Sep 17 00:00:00 2001 From: Denis Date: Tue, 27 Feb 2018 04:51:55 +0300 Subject: [PATCH 6/7] BAEL-1462 Kotlin DI with Kodein (#3544) * BAEL-1462 Kotlin DI with Kodein * BAEL-1462 Kotlin DI with Kodein * applied editor's suggestions * removed unnecessary curly braces * Moved kodein files into core-kotlin as per editor's review * Using assertj instead of junit assertions as per editor's instruction --- core-kotlin/pom.xml | 15 +- .../com/baeldung/kotlin/kodein/Controller.kt | 3 + .../kotlin/com/baeldung/kotlin/kodein/Dao.kt | 3 + .../com/baeldung/kotlin/kodein/JdbcDao.kt | 3 + .../com/baeldung/kotlin/kodein/MongoDao.kt | 3 + .../com/baeldung/kotlin/kodein/Service.kt | 3 + .../baeldung/kotlin/kodein/KodeinUnitTest.kt | 191 ++++++++++++++++++ 7 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/Controller.kt create mode 100644 core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/Dao.kt create mode 100644 core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/JdbcDao.kt create mode 100644 core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/MongoDao.kt create mode 100644 core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/Service.kt create mode 100644 core-kotlin/src/test/kotlin/com/baeldung/kotlin/kodein/KodeinUnitTest.kt diff --git a/core-kotlin/pom.xml b/core-kotlin/pom.xml index 33bdbf719f..36298ca084 100644 --- a/core-kotlin/pom.xml +++ b/core-kotlin/pom.xml @@ -76,7 +76,18 @@ mockito-kotlin ${mockito-kotlin.version} test - + + + com.github.salomonbrys.kodein + kodein + ${kodein.version} + + + org.assertj + assertj-core + ${assertj.version} + test + @@ -189,11 +200,13 @@ 1.1.2 0.15 1.5.0 + 4.1.0 5.0.0 1.0.0 4.12.0 4.12 + 3.9.1 diff --git a/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/Controller.kt b/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/Controller.kt new file mode 100644 index 0000000000..721bdb04bc --- /dev/null +++ b/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/Controller.kt @@ -0,0 +1,3 @@ +package com.baeldung.kotlin.kodein + +class Controller(private val service : Service) \ No newline at end of file diff --git a/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/Dao.kt b/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/Dao.kt new file mode 100644 index 0000000000..a0be7ef0e0 --- /dev/null +++ b/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/Dao.kt @@ -0,0 +1,3 @@ +package com.baeldung.kotlin.kodein + +interface Dao \ No newline at end of file diff --git a/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/JdbcDao.kt b/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/JdbcDao.kt new file mode 100644 index 0000000000..0a09b95dbf --- /dev/null +++ b/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/JdbcDao.kt @@ -0,0 +1,3 @@ +package com.baeldung.kotlin.kodein + +class JdbcDao : Dao \ No newline at end of file diff --git a/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/MongoDao.kt b/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/MongoDao.kt new file mode 100644 index 0000000000..06436fcd21 --- /dev/null +++ b/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/MongoDao.kt @@ -0,0 +1,3 @@ +package com.baeldung.kotlin.kodein + +class MongoDao : Dao \ No newline at end of file diff --git a/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/Service.kt b/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/Service.kt new file mode 100644 index 0000000000..bb24a5cc21 --- /dev/null +++ b/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/Service.kt @@ -0,0 +1,3 @@ +package com.baeldung.kotlin.kodein + +class Service(private val dao: Dao, private val tag: String) \ No newline at end of file diff --git a/core-kotlin/src/test/kotlin/com/baeldung/kotlin/kodein/KodeinUnitTest.kt b/core-kotlin/src/test/kotlin/com/baeldung/kotlin/kodein/KodeinUnitTest.kt new file mode 100644 index 0000000000..7776eebd52 --- /dev/null +++ b/core-kotlin/src/test/kotlin/com/baeldung/kotlin/kodein/KodeinUnitTest.kt @@ -0,0 +1,191 @@ +package com.baeldung.kotlin.kodein + +import com.github.salomonbrys.kodein.* +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test + +class KodeinUnitTest { + + class InMemoryDao : Dao + + @Test + fun whenSingletonBinding_thenSingleInstanceIsCreated() { + var created = false + val kodein = Kodein { + bind() with singleton { + created = true + MongoDao() + } + } + + assertThat(created).isFalse() + + val dao1: Dao = kodein.instance() + + assertThat(created).isTrue() + + val dao2: Dao = kodein.instance() + + assertThat(dao1).isSameAs(dao2) + } + + @Test + fun whenFactoryBinding_thenNewInstanceIsCreated() { + val kodein = Kodein { + bind() with singleton { MongoDao() } + bind() with factory { tag: String -> Service(instance(), tag) } + } + val service1: Service = kodein.with("myTag").instance() + val service2: Service = kodein.with("myTag").instance() + + assertThat(service1).isNotSameAs(service2) + } + + @Test + fun whenProviderBinding_thenNewInstanceIsCreated() { + val kodein = Kodein { + bind() with provider { MongoDao() } + } + val dao1: Dao = kodein.instance() + val dao2: Dao = kodein.instance() + + assertThat(dao1).isNotSameAs(dao2) + } + + @Test + fun whenTaggedBinding_thenMultipleInstancesOfSameTypeCanBeRegistered() { + val kodein = Kodein { + bind("dao1") with singleton { MongoDao() } + bind("dao2") with singleton { MongoDao() } + } + val dao1: Dao = kodein.instance("dao1") + val dao2: Dao = kodein.instance("dao2") + + assertThat(dao1).isNotSameAs(dao2) + } + + @Test + fun whenEagerSingletonBinding_thenCreationIsEager() { + var created = false + val kodein = Kodein { + bind() with eagerSingleton { + created = true + MongoDao() + } + } + + assertThat(created).isTrue() + val dao1: Dao = kodein.instance() + val dao2: Dao = kodein.instance() + + assertThat(dao1).isSameAs(dao2) + } + + @Test + fun whenMultitonBinding_thenInstancesAreReused() { + val kodein = Kodein { + bind() with singleton { MongoDao() } + bind() with multiton { tag: String -> Service(instance(), tag) } + } + val service1: Service = kodein.with("myTag").instance() + val service2: Service = kodein.with("myTag").instance() + + assertThat(service1).isSameAs(service2) + } + + @Test + fun whenInstanceBinding_thenItIsReused() { + val dao = MongoDao() + val kodein = Kodein { + bind() with instance(dao) + } + val fromContainer: Dao = kodein.instance() + + assertThat(dao).isSameAs(fromContainer) + } + + @Test + fun whenConstantBinding_thenItIsAvailable() { + val kodein = Kodein { + constant("magic") with 42 + } + val fromContainer: Int = kodein.instance("magic") + + assertThat(fromContainer).isEqualTo(42) + } + + @Test + fun whenUsingModules_thenTransitiveDependenciesAreSuccessfullyResolved() { + val jdbcModule = Kodein.Module { + bind() with singleton { JdbcDao() } + } + val kodein = Kodein { + import(jdbcModule) + bind() with singleton { Controller(instance()) } + bind() with singleton { Service(instance(), "myService") } + } + + val dao: Dao = kodein.instance() + assertThat(dao).isInstanceOf(JdbcDao::class.java) + } + + @Test + fun whenComposition_thenBeansAreReUsed() { + val persistenceContainer = Kodein { + bind() with singleton { MongoDao() } + } + val serviceContainer = Kodein { + extend(persistenceContainer) + bind() with singleton { Service(instance(), "myService") } + } + val fromPersistence: Dao = persistenceContainer.instance() + val fromService: Dao = serviceContainer.instance() + + assertThat(fromPersistence).isSameAs(fromService) + } + + @Test + fun whenOverriding_thenRightBeanIsUsed() { + val commonModule = Kodein.Module { + bind() with singleton { MongoDao() } + bind() with singleton { Service(instance(), "myService") } + } + val testContainer = Kodein { + import(commonModule) + bind(overrides = true) with singleton { InMemoryDao() } + } + val dao: Dao = testContainer.instance() + + assertThat(dao).isInstanceOf(InMemoryDao::class.java) + } + + @Test + fun whenMultiBinding_thenWorks() { + val kodein = Kodein { + bind() from setBinding() + bind().inSet() with singleton { MongoDao() } + bind().inSet() with singleton { JdbcDao() } + } + val daos: Set = kodein.instance() + + assertThat(daos.map { it.javaClass as Class<*> }).containsOnly(MongoDao::class.java, JdbcDao::class.java) + } + + @Test + fun whenInjector_thenWorks() { + class Controller2 { + private val injector = KodeinInjector() + val service: Service by injector.instance() + fun injectDependencies(kodein: Kodein) = injector.inject(kodein) + } + + val kodein = Kodein { + bind() with singleton { MongoDao() } + bind() with singleton { Service(instance(), "myService") } + } + val controller = Controller2() + controller.injectDependencies(kodein) + + assertThat(controller.service).isNotNull + } +} \ No newline at end of file From 9801a08599022643177a5e45b92da7e2f15f4fae Mon Sep 17 00:00:00 2001 From: Carlo Corti Date: Tue, 27 Feb 2018 14:33:28 +0100 Subject: [PATCH 7/7] BAEL-1441: Method Handles in Java 9 (#3565) * Added MethodHandles API code * Added Reflection API example for comparison --- .../baeldung/java9/methodhandles/Book.java | 17 ++ .../methodhandles/MethodHandlesTest.java | 152 ++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 core-java-9/src/main/java/com/baeldung/java9/methodhandles/Book.java create mode 100644 core-java-9/src/test/java/com/baeldung/java9/methodhandles/MethodHandlesTest.java diff --git a/core-java-9/src/main/java/com/baeldung/java9/methodhandles/Book.java b/core-java-9/src/main/java/com/baeldung/java9/methodhandles/Book.java new file mode 100644 index 0000000000..479f62cb4e --- /dev/null +++ b/core-java-9/src/main/java/com/baeldung/java9/methodhandles/Book.java @@ -0,0 +1,17 @@ +package com.baeldung.java9.methodhandles; + +public class Book { + + String id; + String title; + + public Book(String id, String title) { + this.id = id; + this.title = title; + } + + @SuppressWarnings("unused") + private String formatBook() { + return id + " > " + title; + } +} diff --git a/core-java-9/src/test/java/com/baeldung/java9/methodhandles/MethodHandlesTest.java b/core-java-9/src/test/java/com/baeldung/java9/methodhandles/MethodHandlesTest.java new file mode 100644 index 0000000000..7646755358 --- /dev/null +++ b/core-java-9/src/test/java/com/baeldung/java9/methodhandles/MethodHandlesTest.java @@ -0,0 +1,152 @@ +package com.baeldung.java9.methodhandles; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.*; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.invoke.WrongMethodTypeException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; + +/** + * Test case for the {@link MethodHandles} API + */ +public class MethodHandlesTest { + + @Test + public void givenConcatMethodHandle_whenInvoked_thenCorrectlyConcatenated() throws Throwable { + + MethodHandles.Lookup publicLookup = MethodHandles.publicLookup(); + MethodType mt = MethodType.methodType(String.class, String.class); + MethodHandle concatMH = publicLookup.findVirtual(String.class, "concat", mt); + + String output = (String) concatMH.invoke("Effective ", "Java"); + + assertEquals("Effective Java", output); + } + + @Test + public void givenAsListMethodHandle_whenInvokingWithArguments_thenCorrectlyInvoked() throws Throwable { + MethodHandles.Lookup publicLookup = MethodHandles.publicLookup(); + MethodType mt = MethodType.methodType(List.class, Object[].class); + MethodHandle asListMH = publicLookup.findStatic(Arrays.class, "asList", mt); + + List list = (List) asListMH.invokeWithArguments(1, 2); + + assertThat(Arrays.asList(1, 2), is(list)); + } + + @Test + public void givenConstructorMethodHandle_whenInvoked_thenObjectCreatedCorrectly() throws Throwable { + MethodHandles.Lookup publicLookup = MethodHandles.publicLookup(); + MethodType mt = MethodType.methodType(void.class, String.class); + MethodHandle newIntegerMH = publicLookup.findConstructor(Integer.class, mt); + + Integer integer = (Integer) newIntegerMH.invoke("1"); + + assertEquals(1, integer.intValue()); + } + + @Test + public void givenAFieldWithoutGetter_whenCreatingAGetter_thenCorrectlyInvoked() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle getTitleMH = lookup.findGetter(Book.class, "title", String.class); + + Book book = new Book("ISBN-1234", "Effective Java"); + + assertEquals("Effective Java", getTitleMH.invoke(book)); + } + + @Test + public void givenPrivateMethod_whenCreatingItsMethodHandle_thenCorrectlyInvoked() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + Method formatBookMethod = Book.class.getDeclaredMethod("formatBook"); + formatBookMethod.setAccessible(true); + + MethodHandle formatBookMH = lookup.unreflect(formatBookMethod); + + Book book = new Book("ISBN-123", "Java in Action"); + + assertEquals("ISBN-123 > Java in Action", formatBookMH.invoke(book)); + } + + @Test + public void givenReplaceMethod_whenUsingReflectionAndInvoked_thenCorrectlyReplaced() throws Throwable { + Method replaceMethod = String.class.getMethod("replace", char.class, char.class); + + String string = (String) replaceMethod.invoke("jovo", 'o', 'a'); + + assertEquals("java", string); + } + + @Test + public void givenReplaceMethodHandle_whenInvoked_thenCorrectlyReplaced() throws Throwable { + MethodHandles.Lookup publicLookup = MethodHandles.publicLookup(); + MethodType mt = MethodType.methodType(String.class, char.class, char.class); + MethodHandle replaceMH = publicLookup.findVirtual(String.class, "replace", mt); + + String replacedString = (String) replaceMH.invoke("jovo", Character.valueOf('o'), 'a'); + + assertEquals("java", replacedString); + } + + @Test + public void givenReplaceMethodHandle_whenInvokingExact_thenCorrectlyReplaced() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodType mt = MethodType.methodType(String.class, char.class, char.class); + MethodHandle replaceMH = lookup.findVirtual(String.class, "replace", mt); + + String s = (String) replaceMH.invokeExact("jovo", 'o', 'a'); + + assertEquals("java", s); + } + + @Test + public void givenSumMethodHandle_whenInvokingExact_thenSumIsCorrect() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodType mt = MethodType.methodType(int.class, int.class, int.class); + MethodHandle sumMH = lookup.findStatic(Integer.class, "sum", mt); + + int sum = (int) sumMH.invokeExact(1, 11); + + assertEquals(12, sum); + } + + @Test(expected = WrongMethodTypeException.class) + public void givenSumMethodHandleAndIncompatibleArguments_whenInvokingExact_thenException() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodType mt = MethodType.methodType(int.class, int.class, int.class); + MethodHandle sumMH = lookup.findStatic(Integer.class, "sum", mt); + + sumMH.invokeExact(Integer.valueOf(1), 11); + } + + @Test + public void givenSpreadedEqualsMethodHandle_whenInvokedOnArray_thenCorrectlyEvaluated() throws Throwable { + MethodHandles.Lookup publicLookup = MethodHandles.publicLookup(); + MethodType mt = MethodType.methodType(boolean.class, Object.class); + MethodHandle equalsMH = publicLookup.findVirtual(String.class, "equals", mt); + + MethodHandle methodHandle = equalsMH.asSpreader(Object[].class, 2); + + assertTrue((boolean) methodHandle.invoke(new Object[] { "java", "java" })); + assertFalse((boolean) methodHandle.invoke(new Object[] { "java", "jova" })); + } + + @Test + public void givenConcatMethodHandle_whenBindToAString_thenCorrectlyConcatenated() throws Throwable { + MethodHandles.Lookup publicLookup = MethodHandles.publicLookup(); + MethodType mt = MethodType.methodType(String.class, String.class); + MethodHandle concatMH = publicLookup.findVirtual(String.class, "concat", mt); + + MethodHandle bindedConcatMH = concatMH.bindTo("Hello "); + + assertEquals("Hello World!", bindedConcatMH.invoke("World!")); + } +}