package com.codepoetics.phantompojo; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.Test; import java.io.IOException; import java.util.*; import java.util.function.Supplier; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; public class PhantomTest { public interface Address extends PhantomPojo<Address.Builder> { Lens<Address, String> postcode = Lens.onPhantom(Address::getPostcode, Builder::withPostcode); interface Builder extends Supplier<Address> { Builder withAddressLines(String...addressLines); Builder withAddressLines(Collection<String> addressLines); Builder withPostcode(String postcode); } static Builder builder() { return PhantomBuilder.building(Address.class); } List<String> getAddressLines(); String getPostcode(); } public interface Person extends PhantomPojo<Person.Builder> { Lens<Person, Integer> age = Lens.onPhantom(Person::getAge, Builder::withAge); Lens<Person, Address> address = Lens.onPhantom(Person::getAddress, Builder::withAddress); interface Builder extends Supplier<Person> { Builder withName(String name); Builder withAge(int age); Builder withLotteryNumbers(int...numbers); Builder withFriends(Builder...friendBuilders); Builder withAddress(Address address); Builder withAddress(Address.Builder addressBuilder); Builder withNicknames(String...nicknames); Builder withNicknames(Collection<String> nicknames); } static Builder builder() { return PhantomBuilder.building(Person.class).withAge(-1); } String getName(); int getAge(); Set<Integer> getLotteryNumbers(); List<Person> getFriends(); Address getAddress(); Set<String> getNicknames(); @JsonIgnore default String getPostcode() { return getAddress().getPostcode(); } @JsonCreator static Person create( Map<String, Object> properties) { return PhantomPojo.wrapping(properties).with(Person.class); } } @Test public void build_get_update_get() { Person test = Person.builder() .withName("Henry") .withAge(42) .withLotteryNumbers(23, 12, 15, 52, 19) .withFriends(Person.builder() .withName("Jerry") .withAge(33) .withFriends()) .get(); assertThat(test.getName(), equalTo("Henry")); assertThat(test.getLotteryNumbers(), hasItems(23, 12, 15, 52, 19)); assertThat(test.getFriends().get(0).getName(), equalTo("Jerry")); assertThat(test.getFriends().get(0).getAge(), equalTo(33)); Person henrietta = test.update().withName("Henrietta").get(); Person olderHenrietta = Person.age.update(henrietta, i -> i + 1); assertThat(test.getName(), equalTo("Henry")); assertThat(olderHenrietta.getName(), equalTo("Henrietta")); assertThat(olderHenrietta.getAge(), equalTo(43)); assertThat(olderHenrietta.getFriends().get(0).getName(), equalTo("Jerry")); } @Test public void equality() { assertThat(Person.builder().withName("Phyllis").withAge(42).get(), equalTo(Person.builder().withName("Phyllis").withAge(42).get())); assertThat(Person.builder().withName("Phyllis").withAge(42).get(), not(equalTo(Person.builder().withName("Phyllis").withAge(43).get()))); } @Test public void to_string() { assertThat(Person.builder().withName("Roger").withAge(13).get().toString(), allOf( containsString("Person"), containsString("name=Roger"), containsString("age=13"))); } @Test public void lenses() { Person harry = Person.builder() .withName("Harry") .withAddress(Address.builder() .withAddressLines("23 Acacia Avenue", "Surbiton") .withPostcode("VB6 5UX")) .get(); Lens<Person, String> addressPostcode = Person.address.andThen(Address.postcode); assertThat(addressPostcode.get(harry), equalTo("VB6 5UX")); Person harryMoved = addressPostcode.set(harry, "RA8 81T"); assertThat(harryMoved.getAddress().getPostcode(), equalTo("RA8 81T")); } @Test public void property_map() { Person harry = Person.builder() .withName("Harry") .withAddress(Address.builder() .withAddressLines("23 Acacia Avenue", "Surbiton") .withPostcode("VB6 5UX")) .get(); Map<String, Object> properties = harry.properties(); Person rewrapped = PhantomPojo.wrapping(properties).with(Person.class); assertThat(rewrapped.getName(), equalTo("Harry")); } @Test public void automatic_promotion_from_map_to_phantom() { Map<String, Object> addressProperties = new HashMap<>(); addressProperties.put("addressLines", Arrays.asList("67 Penguin Street", "Cinderford")); addressProperties.put("postcode", "RA8 81T"); Map<String, Object> personProperties = new HashMap<>(); personProperties.put("name", "Harry"); personProperties.put("age", 37); personProperties.put("address", addressProperties); personProperties.put("lotteryNumbers", new int[] { 1, 2, 3, 4 }); Person person = PhantomPojo.wrapping(personProperties).with(Person.class); assertThat(person.getLotteryNumbers(), hasItems(1, 2, 3, 4)); assertThat(person.getAddress().getPostcode(), equalTo("RA8 81T")); } @Test public void automatic_promotion_from_list_of_maps_to_list_of_phantoms() { Map<String, Object> harryProperties = new HashMap<>(); harryProperties.put("name", "Harry"); harryProperties.put("age", 37); Map<String, Object> sallyProperties = new HashMap<>(); sallyProperties.put("name", "Sally"); sallyProperties.put("age", 38); Map<String, Object> steveProperties = new HashMap<>(); steveProperties.put("name", "Steve"); steveProperties.put("age", 29); harryProperties.put("friends", Arrays.asList(sallyProperties, steveProperties)); Person harry = PhantomPojo.wrapping(harryProperties).with(Person.class); Person sally = PhantomPojo.wrapping(sallyProperties).with(Person.class); Person steve = PhantomPojo.wrapping(steveProperties).with(Person.class); assertThat(harry.getFriends(), hasItems(sally, steve)); } @Test public void promotion_handles_sets() { Map<String, Object> harryProperties = new HashMap<>(); harryProperties.put("name", "Harry"); harryProperties.put("age", 37); Set<String> nicknames = new HashSet<>(); nicknames.add("'orrible 'arry"); nicknames.add("Harry the Hat"); harryProperties.put("nicknames", nicknames); Person harry = PhantomPojo.wrapping(harryProperties).with(Person.class); assertThat(harry.getNicknames(), hasItems("'orrible 'arry", "Harry the Hat")); } @Test public void promotion_converts_between_collection_types() { Map<String, Object> harryProperties = new HashMap<>(); harryProperties.put("name", "Harry"); harryProperties.put("age", 37); Map<String, Object> sallyProperties = new HashMap<>(); sallyProperties.put("name", "Sally"); sallyProperties.put("age", 38); Map<String, Object> steveProperties = new HashMap<>(); steveProperties.put("name", "Steve"); steveProperties.put("age", 29); Set<Map<String, Object>> friendSet = new HashSet<>(); friendSet.add(sallyProperties); friendSet.add(steveProperties); harryProperties.put("friends", friendSet); Person harry = PhantomPojo.wrapping(harryProperties).with(Person.class); Person sally = PhantomPojo.wrapping(sallyProperties).with(Person.class); Person steve = PhantomPojo.wrapping(steveProperties).with(Person.class); assertThat(harry.getFriends(), hasItems(sally, steve)); } @Test public void default_methods() { Person harry = Person.builder() .withName("Harry") .withAddress(Address.builder() .withAddressLines("23 Acacia Avenue", "Surbiton") .withPostcode("VB6 5UX")) .get(); assertThat(harry.getPostcode(), equalTo("VB6 5UX")); } @Test public void jackson_mapping() throws IOException { ObjectMapper mapper = new ObjectMapper(); Person harry = Person.builder() .withName("Harry") .withAge(42) .withFriends( Person.builder().withName("Fred"), Person.builder().withName("Jerry").withAge(54)) .withAddress(Address.builder() .withAddressLines("23 Acacia Avenue", "Surbiton") .withPostcode("VB6 5UX")) .get(); String json = mapper.writeValueAsString(harry); Person deserialised = mapper.readValue(json, Person.class); assertThat(harry, equalTo(deserialised)); } @Test public void flexible_collection_creation() { Set<String> nicknameSet = new HashSet<>(); nicknameSet.add("'orrible 'arry"); nicknameSet.add("Harry the Hat"); assertThat(Person.builder() .withName("Harry") .withNicknames("Harry the Hat", "'orrible 'arry") .get().getNicknames(), equalTo(nicknameSet)); assertThat(Person.builder() .withName("Harry") .withNicknames(Arrays.asList("Harry the Hat", "'orrible 'arry")) .get().getNicknames(), equalTo(nicknameSet)); assertThat(Person.builder() .withName("Harry") .withNicknames(nicknameSet) .get().getNicknames(), equalTo(nicknameSet)); } @Test public void hashcode() { Person person1 = Person.builder().withName("Person").withLotteryNumbers(1, 2, 3).get(); Person person2 = Person.builder().withName("Person").withLotteryNumbers(3, 2, 1).get(); assertThat(person1, equalTo(person2)); assertThat(person1.hashCode(), equalTo(person2.hashCode())); } }