Last active
August 29, 2015 14:23
-
-
Save tkareine/7c7dd466e1d1cf4dc369 to your computer and use it in GitHub Desktop.
Before and after refactoring method control flow with a custom monad, in Java 7. Method LTOfficeHeuristics#findOffices is the main entry point. LTOfficeHeuristics_before.java is the original implementation with explicit state handling and early returns. LTOfficeHeuristics_after.java is the refactored version with Narrowing monad. LTOfficeHeurist…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.tkareine.demo.service.csv.parser; | |
import com.google.common.base.Function; | |
import com.google.common.base.Functions; | |
import com.google.common.base.Predicate; | |
import com.google.common.collect.ImmutableSet; | |
import com.google.common.collect.Sets; | |
import com.google.inject.Inject; | |
import org.tkareine.demo.model.Address; | |
import org.tkareine.demo.model.Field; | |
import org.tkareine.demo.model.Office; | |
import org.tkareine.demo.service.OfficeService; | |
import org.tkareine.demo.util.Narrowing; | |
import org.tkareine.demo.util.Patterns; | |
import java.util.Set; | |
import java.util.regex.Matcher; | |
import java.util.regex.Pattern; | |
import static com.google.common.base.Strings.isNullOrEmpty; | |
import static com.google.common.base.Strings.nullToEmpty; | |
public class LTOfficeHeuristics { | |
public final static Pattern STREET_NAME_GUESS_PATTERN = Pattern.compile("(?:\\p{L}{3,}[\\p{L}-_ ]{3,})*\\p{L}{3,}"); | |
private final OfficeService officeService; | |
@Inject | |
public LTOfficeHeuristics(OfficeService officeService) { | |
this.officeService = officeService; | |
} | |
public Set<Office> findOffices(Address address, String nameCandidate) { | |
nameCandidate = nullToEmpty(nameCandidate); | |
return narrowOfficesWithFullAddress(address, nameCandidate) | |
.flatMap(narrowOfficesWithStreetNameAndPostalCodeFunction(address, nameCandidate)) | |
.flatMap(narrowOfficesWithStreetNameAndPostOfficeFunction(address, nameCandidate)) | |
.flatMap(narrowOfficesWithPostOfficeFunction(address, nameCandidate)) | |
.narrowed; | |
} | |
private Narrowing<Office> narrowOfficesWithFullAddress(final Address address, final String nameCandidate) { | |
Narrowing<Office> nrw = Narrowing.of(ImmutableSet.<Office>of()); | |
if (address.hasAllFields()) { | |
return nrw | |
.flatMap(findOfficesWithFullAddressFunction(address, Office.ADDRESS_STREET, Office.ADDRESS_POSTAL_CODE, Office.ADDRESS_POST_OFFICE, nameCandidate)) | |
.flatMap(findOfficesWithFullAddressFunction(address, Office.MAILING_ADDRESS_STREET, Office.MAILING_ADDRESS_POSTAL_CODE, Office.MAILING_ADDRESS_POST_OFFICE, nameCandidate)) | |
.flatMap(findOfficesWithFullAddressFunction(address, Office.ADDRESS_STREET, Office.MAILING_ADDRESS_POSTAL_CODE, Office.MAILING_ADDRESS_POST_OFFICE, nameCandidate)); | |
} else { | |
return nrw; | |
} | |
} | |
private Function<Set<Office>, Narrowing<Office>> narrowOfficesWithStreetNameAndPostalCodeFunction(final Address address, final String nameCandidate) { | |
return new Function<Set<Office>, Narrowing<Office>>() { | |
@Override | |
public Narrowing<Office> apply(Set<Office> found) { | |
Narrowing<Office> nrw = Narrowing.of(found); | |
if (address.hasStreetAndPostalCode()) { | |
String streetName = streetNameOrNullOf(address.getStreet()); | |
if (streetName != null) { | |
return nrw.flatMap(findOfficesWithStreetNameAndPostalCodeFunction(streetName, address.getPostalCode(), nameCandidate)); | |
} | |
} | |
return nrw; | |
} | |
}; | |
} | |
private Function<Set<Office>, Narrowing<Office>> narrowOfficesWithStreetNameAndPostOfficeFunction(final Address address, final String nameCandidate) { | |
return new Function<Set<Office>, Narrowing<Office>>() { | |
@Override | |
public Narrowing<Office> apply(Set<Office> found) { | |
Narrowing<Office> nrw = Narrowing.of(found); | |
if (address.hasStreetAndPostOffice()) { | |
String streetName = streetNameOrNullOf(address.getStreet()); | |
if (streetName != null) { | |
return nrw.flatMap(findOfficesWithStreetNameAndPostOfficeFunction(streetName, address.getPostOffice(), nameCandidate)); | |
} | |
} | |
return nrw; | |
} | |
}; | |
} | |
private Function<Set<Office>, Narrowing<Office>> narrowOfficesWithPostOfficeFunction(final Address address, final String nameCandidate) { | |
return new Function<Set<Office>, Narrowing<Office>>() { | |
@Override | |
public Narrowing<Office> apply(Set<Office> found) { | |
Narrowing<Office> nrw = Narrowing.of(found); | |
String postOffice = address.getPostOffice(); | |
if (!postOffice.isEmpty()) { | |
return nrw.flatMap(findOfficesWithPostOfficeFunction(postOffice, nameCandidate)); | |
} | |
return nrw; | |
} | |
}; | |
} | |
private Function<Set<Office>, Narrowing<Office>> findOfficesWithFullAddressFunction(final Address address, final String streetField, final String postalCodeField, final String postOfficeField, String nameCandidate) { | |
return Functions.compose(narrowManyOfficesByNameFunction(nameCandidate), new Function<Set<Office>, Set<Office>>() { | |
@Override | |
public Set<Office> apply(Set<Office> ignored) { | |
return ImmutableSet.copyOf(officeService.createQuery() | |
.field(streetField).startsWithIgnoreCase(Pattern.quote(address.getStreet())) | |
.field(postalCodeField).equal(address.getPostalCode()) | |
.field(postOfficeField).startsWithIgnoreCase(Pattern.quote(address.getPostOffice())) | |
.order(Office.NAME)); // sort by name ascending to ensure predictability | |
} | |
}); | |
} | |
private Function<Set<Office>, Narrowing<Office>> findOfficesWithStreetNameAndPostalCodeFunction(final String streetName, final String postalCode, String nameCandidate) { | |
return Functions.compose(narrowManyOfficesByNameFunction(nameCandidate), new Function<Set<Office>, Set<Office>>() { | |
@Override | |
public Set<Office> apply(Set<Office> ignored) { | |
return ImmutableSet.copyOf(officeService.createQuery() | |
.field(Office.ADDRESS_STREET).containsIgnoreCase(Pattern.quote(streetName)) | |
.filter(Office.ADDRESS_POSTAL_CODE, postalCode) | |
.order(Office.NAME)); // sort by name ascending to ensure predictability | |
} | |
}); | |
} | |
private Function<Set<Office>, Narrowing<Office>> findOfficesWithStreetNameAndPostOfficeFunction(final String streetName, final String postOffice, String nameCandidate) { | |
return Functions.compose(narrowManyOfficesByNameFunction(nameCandidate), new Function<Set<Office>, Set<Office>>() { | |
@Override | |
public Set<Office> apply(Set<Office> ignored) { | |
return ImmutableSet.copyOf(officeService.createQuery() | |
.field(Office.ADDRESS_STREET).containsIgnoreCase(Pattern.quote(streetName)) | |
.filter(Office.ADDRESS_POST_OFFICE, Patterns.matchesFullyCaseInsensitive(postOffice)) | |
.order(Office.NAME)); // sort by name ascending to ensure predictability | |
} | |
}); | |
} | |
private Function<Set<Office>, Narrowing<Office>> findOfficesWithPostOfficeFunction(final String postOffice, String nameCandidate) { | |
return Functions.compose(narrowManyOfficesByNameFunction(nameCandidate), new Function<Set<Office>, Set<Office>>() { | |
@Override | |
public Set<Office> apply(Set<Office> ignored) { | |
return ImmutableSet.copyOf(officeService.createQuery() | |
.filter(Office.ADDRESS_POST_OFFICE, Patterns.matchesFullyCaseInsensitive(postOffice)) | |
.order(Office.NAME)); // sort by name ascending to ensure predictability | |
} | |
}); | |
} | |
private static Function<Set<Office>, Narrowing<Office>> narrowManyOfficesByNameFunction(final String nameCandidate) { | |
return new Function<Set<Office>, Narrowing<Office>>() { | |
@Override | |
public Narrowing<Office> apply(Set<Office> found) { | |
return found.size() > 1 && !nameCandidate.isEmpty() | |
? narrowOfficesByNameFunction(nameCandidate, found) | |
: Narrowing.of(found); | |
} | |
}; | |
} | |
private static Narrowing<Office> narrowOfficesByNameFunction(final String nameCandidate, final Set<Office> foundWithoutNameNarrowing) { | |
return Narrowing.of(filterOfficesByFieldContainsIgnoringCase(Office.AD_INFO, nameCandidate, foundWithoutNameNarrowing)) | |
.map(filterOfficesByFieldEqualsIgnoringCaseFunction(Office.AD_INFO, nameCandidate)) | |
.flatMap(new Function<Set<Office>, Narrowing<Office>>() { | |
@Override | |
public Narrowing<Office> apply(Set<Office> ignored) { | |
return Narrowing.of(filterOfficesByFieldContainsIgnoringCase(Office.NAME, nameCandidate, foundWithoutNameNarrowing)) | |
.map(filterOfficesByFieldEqualsIgnoringCaseFunction(Office.NAME, nameCandidate)); | |
} | |
}); | |
} | |
private static Set<Office> filterOfficesByFieldContainsIgnoringCase(final String fieldName, final String fieldValue, final Set<Office> found) { | |
return Sets.filter(found, officeFieldContainsIgnoringCasePredicate(fieldName, fieldValue)); | |
} | |
private static Function<Set<Office>, Set<Office>> filterOfficesByFieldEqualsIgnoringCaseFunction(final String fieldName, final String fieldValue) { | |
return new Function<Set<Office>, Set<Office>>() { | |
@Override | |
public Set<Office> apply(Set<Office> found) { | |
return Sets.filter(found, officeFieldEqualsIgnoringCasePredicate(fieldName, fieldValue)); | |
} | |
}; | |
} | |
private static Predicate<Office> officeFieldContainsIgnoringCasePredicate(final String fieldName, String fieldValue) { | |
final String fieldValueLowerCased = fieldValue.toLowerCase(); | |
return new Predicate<Office>() { | |
@Override | |
public boolean apply(Office office) { | |
String value = office.getField(fieldName).getStringValue(); | |
return !isNullOrEmpty(value) && value.toLowerCase().contains(fieldValueLowerCased); | |
} | |
}; | |
} | |
private static Predicate<Office> officeFieldEqualsIgnoringCasePredicate(final String fieldName, final String fieldValue) { | |
return new Predicate<Office>() { | |
@Override | |
public boolean apply(Office office) { | |
String value = office.getField(fieldName).getStringValue(); | |
return !isNullOrEmpty(value) && value.equalsIgnoreCase(fieldValue); | |
} | |
}; | |
} | |
private static String streetNameOrNullOf(String street) { | |
Matcher matcher = STREET_NAME_GUESS_PATTERN.matcher(street); | |
if (matcher.find()) { | |
return street.substring(matcher.start(), matcher.end()); | |
} else { | |
return null; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.tkareine.demo.service.csv.parser; | |
import com.google.common.base.Predicate; | |
import com.google.common.collect.ImmutableSet; | |
import com.google.common.collect.Sets; | |
import com.google.inject.Inject; | |
import org.tkareine.demo.model.Address; | |
import org.tkareine.demo.model.Office; | |
import org.tkareine.demo.service.OfficeService; | |
import org.tkareine.demo.util.Patterns; | |
import java.util.Collection; | |
import java.util.Set; | |
import java.util.regex.Matcher; | |
import java.util.regex.Pattern; | |
import static com.google.common.base.Strings.isNullOrEmpty; | |
import static com.google.common.base.Strings.nullToEmpty; | |
public class LTOfficeHeuristics { | |
public final static Pattern STREET_NAME_GUESS_PATTERN = Pattern.compile("(?:\\p{L}{3,}[\\p{L}-_ ]{3,})*\\p{L}{3,}"); | |
private final OfficeService officeService; | |
@Inject | |
public LTOfficeHeuristics(OfficeService officeService) { | |
this.officeService = officeService; | |
} | |
public Set<Office> findOffices(Address address, String nameCandidate) { | |
nameCandidate = nullToEmpty(nameCandidate); | |
Set<Office> mostAccurateManyMatches = ImmutableSet.of(); | |
if (address.hasAllFields()) { | |
Set<Office> found = findOfficesWithFullAddress(address, Office.ADDRESS_STREET, Office.ADDRESS_POSTAL_CODE, Office.ADDRESS_POST_OFFICE, nameCandidate); | |
if (isExactMatch(found)) { | |
return found; | |
} | |
mostAccurateManyMatches = minNonEmpty(mostAccurateManyMatches, found); | |
found = findOfficesWithFullAddress(address, Office.MAILING_ADDRESS_STREET, Office.MAILING_ADDRESS_POSTAL_CODE, Office.MAILING_ADDRESS_POST_OFFICE, nameCandidate); | |
if (isExactMatch(found)) { | |
return found; | |
} | |
mostAccurateManyMatches = minNonEmpty(mostAccurateManyMatches, found); | |
found = findOfficesWithFullAddress(address, Office.ADDRESS_STREET, Office.MAILING_ADDRESS_POSTAL_CODE, Office.MAILING_ADDRESS_POST_OFFICE, nameCandidate); | |
if (isExactMatch(found)) { | |
return found; | |
} | |
mostAccurateManyMatches = minNonEmpty(mostAccurateManyMatches, found); | |
} | |
if (address.hasStreetAndPostalCode()) { | |
String streetName = streetNameOrNullOf(address.getStreet()); | |
if (streetName != null) { | |
Set<Office> found = findOfficesWithStreetNameAndPostalCode(streetName, address.getPostalCode(), nameCandidate); | |
if (isExactMatch(found)) { | |
return found; | |
} | |
mostAccurateManyMatches = minNonEmpty(mostAccurateManyMatches, found); | |
} | |
} | |
if (address.hasStreetAndPostOffice()) { | |
String streetName = streetNameOrNullOf(address.getStreet()); | |
if (streetName != null) { | |
Set<Office> found = findOfficesWithStreetNameAndPostOffice(streetName, address.getPostOffice(), nameCandidate); | |
if (isExactMatch(found)) { | |
return found; | |
} | |
mostAccurateManyMatches = minNonEmpty(mostAccurateManyMatches, found); | |
} | |
} | |
String postOffice = address.getPostOffice(); | |
if (!postOffice.isEmpty()) { | |
Set<Office> found = findOfficesWithPostOffice(postOffice, nameCandidate); | |
if (isExactMatch(found)) { | |
return found; | |
} | |
mostAccurateManyMatches = minNonEmpty(mostAccurateManyMatches, found); | |
} | |
return mostAccurateManyMatches; | |
} | |
private Set<Office> findOfficesWithFullAddress(Address address, String streetField, String postalCodeField, String postOfficeField, String nameCandidate) { | |
return narrowManyOfficesByName(nameCandidate, ImmutableSet.copyOf(officeService.createQuery() | |
.field(streetField).startsWithIgnoreCase(Pattern.quote(address.getStreet())) | |
.field(postalCodeField).equal(address.getPostalCode()) | |
.field(postOfficeField).startsWithIgnoreCase(Pattern.quote(address.getPostOffice())) | |
.order(Office.NAME))); // sort by name ascending to ensure predictability | |
} | |
private Set<Office> findOfficesWithStreetNameAndPostalCode(String streetName, String postalCode, String nameCandidate) { | |
return narrowManyOfficesByName(nameCandidate, ImmutableSet.copyOf(officeService.createQuery() | |
.field(Office.ADDRESS_STREET).containsIgnoreCase(Pattern.quote(streetName)) | |
.filter(Office.ADDRESS_POSTAL_CODE, postalCode) | |
.order(Office.NAME))); // sort by name ascending to ensure predictability | |
} | |
private Set<Office> findOfficesWithStreetNameAndPostOffice(String streetName, String postOffice, String nameCandidate) { | |
return narrowManyOfficesByName(nameCandidate, ImmutableSet.copyOf(officeService.createQuery() | |
.field(Office.ADDRESS_STREET).containsIgnoreCase(Pattern.quote(streetName)) | |
.filter(Office.ADDRESS_POST_OFFICE, Patterns.matchesFullyCaseInsensitive(postOffice)) | |
.order(Office.NAME))); // sort by name ascending to ensure predictability | |
} | |
private Set<Office> findOfficesWithPostOffice(String postOffice, String nameCandidate) { | |
return narrowManyOfficesByName(nameCandidate, ImmutableSet.copyOf(officeService.createQuery() | |
.filter(Office.ADDRESS_POST_OFFICE, Patterns.matchesFullyCaseInsensitive(postOffice)) | |
.order(Office.NAME))); // sort by name ascending to ensure predictability | |
} | |
private static Set<Office> narrowManyOfficesByName(String nameCandidate, Set<Office> found) { | |
return found.size() > 1 && !nameCandidate.isEmpty() | |
? narrowOfficesByName(nameCandidate, found) | |
: found; | |
} | |
private static Set<Office> narrowOfficesByName(String nameCandidate, Set<Office> found) { | |
Set<Office> mostAccurateManyMatches = ImmutableSet.of(); | |
Set<Office> narrowedByAdInfo = Sets.filter(found, companyAdInfoContainsIgnoringCasePredicate(nameCandidate)); | |
if (isExactMatch(narrowedByAdInfo)) { | |
return narrowedByAdInfo; | |
} | |
mostAccurateManyMatches = minNonEmpty(mostAccurateManyMatches, narrowedByAdInfo); | |
narrowedByAdInfo = Sets.filter(narrowedByAdInfo, companyAdInfoEqualsIgnoringCasePredicate(nameCandidate)); | |
if (isExactMatch(narrowedByAdInfo)) { | |
return narrowedByAdInfo; | |
} | |
mostAccurateManyMatches = minNonEmpty(mostAccurateManyMatches, narrowedByAdInfo); | |
Set<Office> narrowedByName = Sets.filter(found, companyNameContainsIgnoringCasePredicate(nameCandidate)); | |
if (isExactMatch(narrowedByName)) { | |
return narrowedByName; | |
} | |
mostAccurateManyMatches = minNonEmpty(mostAccurateManyMatches, narrowedByName); | |
narrowedByName = Sets.filter(narrowedByName, companyNameEqualsIgnoringCasePredicate(nameCandidate)); | |
if (isExactMatch(narrowedByName)) { | |
return narrowedByName; | |
} | |
mostAccurateManyMatches = minNonEmpty(mostAccurateManyMatches, narrowedByName); | |
return mostAccurateManyMatches; | |
} | |
private static Predicate<Office> companyAdInfoContainsIgnoringCasePredicate(final String name) { | |
return new Predicate<Office>() { | |
@Override | |
public boolean apply(Office office) { | |
return !isNullOrEmpty(office.adInfo) && office.adInfo.toLowerCase().contains(name.toLowerCase()); | |
} | |
}; | |
} | |
private static Predicate<Office> companyAdInfoEqualsIgnoringCasePredicate(final String name) { | |
return new Predicate<Office>() { | |
@Override | |
public boolean apply(Office office) { | |
return !isNullOrEmpty(office.adInfo) && office.adInfo.equalsIgnoreCase(name); | |
} | |
}; | |
} | |
private static Predicate<Office> companyNameContainsIgnoringCasePredicate(final String name) { | |
return new Predicate<Office>() { | |
@Override | |
public boolean apply(Office office) { | |
return !isNullOrEmpty(office.name) && office.name.toLowerCase().contains(name.toLowerCase()); | |
} | |
}; | |
} | |
private static Predicate<Office> companyNameEqualsIgnoringCasePredicate(final String name) { | |
return new Predicate<Office>() { | |
@Override | |
public boolean apply(Office office) { | |
return !isNullOrEmpty(office.name) && office.name.equalsIgnoreCase(name); | |
} | |
}; | |
} | |
private static String streetNameOrNullOf(String street) { | |
Matcher matcher = STREET_NAME_GUESS_PATTERN.matcher(street); | |
if (matcher.find()) { | |
return street.substring(matcher.start(), matcher.end()); | |
} else { | |
return null; | |
} | |
} | |
private static <T> boolean isExactMatch(Collection<T> collection) { | |
return collection.size() == 1; | |
} | |
private static <T> Set<T> minNonEmpty(Set<T> a, Set<T> b) { | |
if (a.isEmpty() && !b.isEmpty()) { | |
return b; | |
} | |
if (!a.isEmpty() && b.isEmpty()) { | |
return a; | |
} | |
if (b.isEmpty()) { | |
return a; | |
} | |
return b.size() < a.size() ? b : a; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.tkareine.demo.service.csv.parser; | |
import com.google.inject.Inject; | |
import org.tkareine.demo.model.Address; | |
import org.tkareine.demo.model.Office; | |
import org.tkareine.demo.service.OfficeService; | |
import org.tkareine.demo.support.GuiceJUnit4ClassRunner; | |
import org.tkareine.demo.support.di.StubExternalServicesMongoModule; | |
import org.junit.Before; | |
import org.junit.Test; | |
import org.junit.runner.RunWith; | |
import static org.assertj.core.api.Assertions.assertThat; | |
@RunWith(GuiceJUnit4ClassRunner.class) | |
@GuiceJUnit4ClassRunner.Modules({StubExternalServicesMongoModule.class}) | |
public class LTOfficeHeuristicsTest { | |
private static final Address FREDA_VISITING_ADDRESS = new Address("Fredrikinkatu 48 A", "00100", "Helsinki"); | |
private static final Address FREDA_MAILING_ADDRESS = new Address("PL 180", "00101", "Vastauslähetys"); | |
private static final Address TURKU_VISITING_ADDRESS = new Address("Eerikinkatu 6 B", "20100", "Turku"); | |
private LTOfficeHeuristics heuristics; | |
private OfficeService officeService; | |
private Office officeFreda; | |
private Office officeFredaYrityspalvelut; | |
private Office officeTurku; | |
@Inject | |
public void init(LTOfficeHeuristics heuristics, OfficeService officeService) { | |
this.heuristics = heuristics; | |
this.officeService = officeService; | |
} | |
@Before | |
public void setup() { | |
officeService.deleteAll(); | |
officeFreda = saveOffice("Toimisto Freda", "ad-frd", FREDA_VISITING_ADDRESS, FREDA_MAILING_ADDRESS); | |
officeFredaYrityspalvelut = saveOffice("Toimisto Freda, yrityspalvelut", "ad-frd-yrityspalvelut", FREDA_VISITING_ADDRESS, null); | |
officeTurku = saveOffice("Toimisto Turku", "ad-trk", TURKU_VISITING_ADDRESS, null); | |
} | |
@Test | |
public void findsByFullVisitingAddress() { | |
assertThat(heuristics.findOffices(FREDA_VISITING_ADDRESS, "")).containsExactly(officeFreda, officeFredaYrityspalvelut); | |
assertThat(heuristics.findOffices(FREDA_VISITING_ADDRESS, null)).containsExactly(officeFreda, officeFredaYrityspalvelut); | |
assertThat(heuristics.findOffices(TURKU_VISITING_ADDRESS, null)).containsExactly(officeTurku); | |
} | |
@Test | |
public void findsByFullVisitingAddressAndNarrowsMatchesByOfficeAdInfo() { | |
assertThat(heuristics.findOffices(FREDA_VISITING_ADDRESS, "frd")).containsExactly(officeFreda, officeFredaYrityspalvelut); | |
assertThat(heuristics.findOffices(FREDA_VISITING_ADDRESS, "-yrityspalvelut")).containsExactly(officeFredaYrityspalvelut); | |
assertThat(heuristics.findOffices(FREDA_VISITING_ADDRESS, "ad")).containsExactly(officeFreda, officeFredaYrityspalvelut); | |
assertThat(heuristics.findOffices(FREDA_VISITING_ADDRESS, "ad-frd")).containsExactly(officeFreda); | |
assertThat(heuristics.findOffices(FREDA_VISITING_ADDRESS, "AD-Frd")).containsExactly(officeFreda); | |
assertThat(heuristics.findOffices(FREDA_VISITING_ADDRESS, "ad-frd-yrityspalvelut")).containsExactly(officeFredaYrityspalvelut); | |
assertThat(heuristics.findOffices(FREDA_VISITING_ADDRESS, "AD-Frd-Yrityspalvelut")).containsExactly(officeFredaYrityspalvelut); | |
} | |
@Test | |
public void findsByFullVisitingAddressAndNarrowsMatchesByOfficeName() { | |
assertThat(heuristics.findOffices(FREDA_VISITING_ADDRESS, "Freda")).containsExactly(officeFreda, officeFredaYrityspalvelut); | |
assertThat(heuristics.findOffices(FREDA_VISITING_ADDRESS, ", yrityspalvelut")).containsExactly(officeFredaYrityspalvelut); | |
assertThat(heuristics.findOffices(FREDA_VISITING_ADDRESS, "Toimisto")).containsExactly(officeFreda, officeFredaYrityspalvelut); | |
assertThat(heuristics.findOffices(FREDA_VISITING_ADDRESS, "toimisto freda")).containsExactly(officeFreda); | |
assertThat(heuristics.findOffices(FREDA_VISITING_ADDRESS, "Toimisto Freda, Yrityspalvelut")).containsExactly(officeFredaYrityspalvelut); | |
} | |
@Test | |
public void findsByFullMailingAddress() { | |
assertThat(heuristics.findOffices(FREDA_MAILING_ADDRESS, null)).containsExactly(officeFreda); | |
} | |
@Test | |
public void findsByFullMailingAddressAndDoesNotNarrowMatchesIfAddressSearchProducesExactMatch() { | |
assertThat(heuristics.findOffices(FREDA_MAILING_ADDRESS, "freda, yrityspalvelut")).containsExactly(officeFreda); | |
assertThat(heuristics.findOffices(FREDA_MAILING_ADDRESS, "freda-yrityspalvelut")).containsExactly(officeFreda); | |
} | |
@Test | |
public void findsByStreetPartOfVisitingAddressAndPostalCodeAndPostOfficePartsOfMailingAddress() { | |
Address address = new Address(FREDA_VISITING_ADDRESS.getStreet(), FREDA_MAILING_ADDRESS.getPostalCode(), FREDA_MAILING_ADDRESS.getPostOffice()); | |
assertThat(heuristics.findOffices(address, null)).containsExactly(officeFreda); | |
} | |
@Test | |
public void findsByStreetPartOfVisitingAddressAndPostalCodeAndPostOfficePartsOfMailingAddressAndDoesNotNarrowMatchesIfAddressSearchProducesExactMatch() { | |
Address address = new Address(FREDA_VISITING_ADDRESS.getStreet(), FREDA_MAILING_ADDRESS.getPostalCode(), FREDA_MAILING_ADDRESS.getPostOffice()); | |
assertThat(heuristics.findOffices(address, "yrityspalvelut")).containsExactly(officeFreda); | |
} | |
@Test | |
public void findsByStreetNameAndPostalCodePartsOfVisitingAddress() { | |
assertThat(heuristics.findOffices(new Address("Eerikinkatu 6 B", "20100", null), null)).containsExactly(officeTurku); | |
assertThat(heuristics.findOffices(new Address("Eerikinkatu 13", "20100", null), null)).containsExactly(officeTurku); | |
assertThat(heuristics.findOffices(new Address("Eerikinkatu", "20100", null), null)).containsExactly(officeTurku); | |
assertThat(heuristics.findOffices(new Address("Fredrikinkatu 48 A", "00100", null), null)).containsExactly(officeFreda, officeFredaYrityspalvelut); | |
assertThat(heuristics.findOffices(new Address("Fredrikinkatu 52", "00100", null), null)).containsExactly(officeFreda, officeFredaYrityspalvelut); | |
assertThat(heuristics.findOffices(new Address("Fredrikinkatu 48", "00100", null), null)).containsExactly(officeFreda, officeFredaYrityspalvelut); | |
} | |
@Test | |
public void findsByStreetNameAndPostalCodePartsOfVisitingAddressAndNarrowsMatchesByOfficeAdInfo() { | |
assertThat(heuristics.findOffices(new Address("Fredrikinkatu 52", "00100", null), "ad-frd")).containsExactly(officeFreda); | |
} | |
@Test | |
public void findsByStreetNameAndPostalCodePartsOfVisitingAddressAndNarrowsMatchesByOfficeName() { | |
assertThat(heuristics.findOffices(new Address("Fredrikinkatu 52", "00100", null), "Toimisto Freda")).containsExactly(officeFreda); | |
} | |
@Test | |
public void findsByStreetNameAndPostOfficePartsOfVisitingAddress() { | |
assertThat(heuristics.findOffices(new Address("Eerikinkatu 6 B", null, "Turku"), null)).containsExactly(officeTurku); | |
assertThat(heuristics.findOffices(new Address("Eerikinkatu 13", null, "Turku"), null)).containsExactly(officeTurku); | |
assertThat(heuristics.findOffices(new Address("Eerikinkatu", null, "Turku"), null)).containsExactly(officeTurku); | |
assertThat(heuristics.findOffices(new Address("Eerikinkatu", null, "TURKU"), null)).containsExactly(officeTurku); | |
assertThat(heuristics.findOffices(new Address("Eerikinkatu", null, "turku"), null)).containsExactly(officeTurku); | |
assertThat(heuristics.findOffices(new Address("Fredrikinkatu 48 A", null, "Helsinki"), null)).containsExactly(officeFreda, officeFredaYrityspalvelut); | |
assertThat(heuristics.findOffices(new Address("Fredrikinkatu 52", null, "Helsinki"), null)).containsExactly(officeFreda, officeFredaYrityspalvelut); | |
assertThat(heuristics.findOffices(new Address("Fredrikinkatu 48", null, "Helsinki"), null)).containsExactly(officeFreda, officeFredaYrityspalvelut); | |
} | |
@Test | |
public void findsByStreetNameAndPostOfficePartsOfVisitingAddressAndNarrowsMatchesByOfficeAdInfo() { | |
assertThat(heuristics.findOffices(new Address("Fredrikinkatu 52", null, "Helsinki"), "ad-frd")).containsExactly(officeFreda); | |
} | |
@Test | |
public void findsByStreetNameAndPostOfficePartsOfVisitingAddressAndNarrowsMatchesByOfficeName() { | |
assertThat(heuristics.findOffices(new Address("Fredrikinkatu 52", null, "Helsinki"), "Toimisto Freda")).containsExactly(officeFreda); | |
} | |
@Test | |
public void findsByPostOffice() { | |
assertThat(heuristics.findOffices(new Address(null, null, "Turku"), null)).containsExactly(officeTurku); | |
assertThat(heuristics.findOffices(new Address(null, null, "TURKU"), null)).containsExactly(officeTurku); | |
assertThat(heuristics.findOffices(new Address(null, null, "turku"), null)).containsExactly(officeTurku); | |
assertThat(heuristics.findOffices(new Address(null, null, "Helsinki"), null)).containsExactly(officeFreda, officeFredaYrityspalvelut); | |
} | |
@Test | |
public void findsByPostOfficeAndNarrowsMatchesByOfficeAdInfo() { | |
assertThat(heuristics.findOffices(new Address(null, null, "Helsinki"), "ad-frd")).containsExactly(officeFreda); | |
} | |
@Test | |
public void findsByPostOfficeAndNarrowsMatchesByOfficeName() { | |
assertThat(heuristics.findOffices(new Address(null, null, "Helsinki"), "Toimisto Freda")).containsExactly(officeFreda); | |
} | |
@Test | |
public void returnsEmptyIfNotFound() { | |
assertThat(heuristics.findOffices(new Address(), null)).isEmpty(); | |
assertThat(heuristics.findOffices(new Address("Kummunkatu 1", "83500", "Outokumpu"), "Outokumpu")).isEmpty(); | |
} | |
private Office saveOffice(String name, String adInfo, Address visitingAddress, Address mailingAddress) { | |
Office office = new Office(name); | |
office.address = visitingAddress; | |
office.mailingAddress = mailingAddress; | |
office.adInfo = adInfo; | |
officeService.save(office); | |
return office; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.tkareine.demo.util; | |
import com.google.common.base.Function; | |
import com.google.common.base.Objects; | |
import java.util.Set; | |
import static com.google.common.base.Preconditions.checkNotNull; | |
public class Narrowing<T> { | |
public final Set<T> narrowed; | |
private Narrowing(Set<T> nrw) { | |
checkNotNull(nrw, "Narrowed input must be non-null"); | |
narrowed = nrw; | |
} | |
public static <T> Narrowing<T> of(Set<T> nrw) { | |
return new Narrowing<>(nrw); | |
} | |
public Narrowing<T> flatMap(Function<Set<T>, Narrowing<T>> fun) { | |
if (isExactMatch()) { | |
return this; | |
} | |
Narrowing<T> other = fun.apply(narrowed); | |
if (other.isExactMatch()) { | |
return other; | |
} | |
return isMoreAccurateThan(other) | |
? this | |
: other; | |
} | |
public Narrowing<T> map(final Function<Set<T>, Set<T>> fun) { | |
return flatMap(new Function<Set<T>, Narrowing<T>>() { | |
@Override | |
public Narrowing<T> apply(Set<T> nrw) { | |
return Narrowing.of(fun.apply(nrw)); | |
} | |
}); | |
} | |
@Override | |
public boolean equals(Object o) { | |
if (o == this) { | |
return true; | |
} | |
if (!(o instanceof Narrowing)) { | |
return false; | |
} | |
Narrowing other = (Narrowing)o; | |
return this.narrowed.equals(other.narrowed); | |
} | |
@Override | |
public String toString() { | |
return Objects.toStringHelper(this).add("narrowed", narrowed).toString(); | |
} | |
private boolean isExactMatch() { | |
return narrowed.size() == 1; | |
} | |
private boolean isMoreAccurateThan(Narrowing<T> other) { | |
return other.narrowed.isEmpty() || | |
(!this.narrowed.isEmpty() && this.narrowed.size() <= other.narrowed.size()); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.tkareine.demo.util; | |
import com.google.common.base.Function; | |
import com.google.common.collect.ImmutableSet; | |
import com.google.common.collect.Sets; | |
import org.junit.Test; | |
import java.util.Set; | |
import static org.assertj.core.api.Assertions.assertThat; | |
public class NarrowingTest { | |
@Test(expected = NullPointerException.class) | |
public void constructorThrowsExceptionOnNullInput() { | |
Narrowing.of(null); | |
} | |
@Test | |
public void equalityIsReflexive() { | |
Narrowing<String> m = Narrowing.of(ImmutableSet.of("lol")); | |
assertThat(m).isEqualTo(m); | |
} | |
@Test | |
public void equalityIsSymmetric() { | |
Narrowing<String> m1 = Narrowing.of(ImmutableSet.of("lol")); | |
Narrowing<String> m2 = Narrowing.of(ImmutableSet.of("lol")); | |
assertThat(m1).isEqualTo(m2); | |
assertThat(m1.equals(m2) && m2.equals(m1)).isTrue(); | |
} | |
@Test | |
public void nonEquality() { | |
Narrowing<String> m1 = Narrowing.of(ImmutableSet.of("lol")); | |
Narrowing<String> m2 = Narrowing.of(ImmutableSet.of("bal")); | |
assertThat(m1).isNotEqualTo(m2); | |
assertThat(m2).isNotEqualTo(m1); | |
assertThat(m1).isNotEqualTo(null); | |
} | |
@Test | |
public void flatMapReturnsFirstExactMatch() { | |
assertThat(Narrowing.of(ImmutableSet.of("ok")) | |
.map(setOfFunction("should not see me")) | |
.narrowed | |
).containsExactly("ok"); | |
assertThat(Narrowing.of(ImmutableSet.of("lol", "bal")) | |
.map(setOfFunction("ok")) | |
.map(setOfFunction("should not see me")) | |
.narrowed | |
).containsExactly("ok"); | |
assertThat(Narrowing.of(ImmutableSet.of("ok")) | |
.map(new Function<Set<String>, Set<String>>() { | |
@Override | |
public Set<String> apply(Set<String> ignored) { | |
throw new IllegalStateException("should not see me"); | |
} | |
}) | |
.narrowed | |
).containsExactly("ok"); | |
} | |
@Test | |
public void flatMapDiscardsEmptySet() { | |
assertThat(Narrowing.of(ImmutableSet.of("lol", "bal")) | |
.map(setOfFunction()) | |
.narrowed | |
).containsExactly("lol", "bal"); | |
assertThat(Narrowing.of(ImmutableSet.<String>of()) | |
.map(setOfFunction("lol", "bal")) | |
.narrowed | |
).containsExactly("lol", "bal"); | |
} | |
@Test | |
public void flatMapTracksMostAccurateNonEmptySetSoFar() { | |
assertThat(Narrowing.of(ImmutableSet.of("lol", "bal", "zap")) | |
.map(setOfFunction("quux", "taat")) | |
.narrowed | |
).containsExactly("quux", "taat"); | |
assertThat(Narrowing.of(ImmutableSet.of("quux", "taat")) | |
.map(setOfFunction("lol", "bal", "zap")) | |
.narrowed | |
).containsExactly("quux", "taat"); | |
} | |
@Test | |
public void flatMapKeepsFirstMostAccurateTrackedNonEmptySet() { | |
assertThat(Narrowing.of(ImmutableSet.of("quux", "taat")) | |
.map(setOfFunction("lol", "bal")) | |
.narrowed | |
).containsExactly("quux", "taat"); | |
} | |
@Test | |
public void flatMapKeepsFirstEmptySetEncountered() { | |
final Set<String> emptySet = ImmutableSet.of(); | |
assertThat(Narrowing.of(emptySet) | |
.map(setOfFunction()) | |
.narrowed | |
).isSameAs(emptySet); | |
} | |
@Test | |
public void respectsLeftIdentityMonadLaw() { | |
Narrowing<String> lhs = Narrowing.of(ImmutableSet.of("lol", "bal", "zap")).flatMap(intersectFunction("quux", "lol", "bal")); | |
Narrowing<String> rhs = intersectFunction("quux", "lol", "bal").apply(ImmutableSet.of("lol", "bal", "zap")); | |
assertThat(lhs).isEqualTo(rhs); | |
} | |
@Test | |
public void respectsRightIdentityMonadLaw() { | |
Narrowing<String> lhs = Narrowing.of(ImmutableSet.of("lol", "bal")); | |
Narrowing<String> rhs = Narrowing.of(ImmutableSet.of("lol", "bal")).flatMap(returnFunction()); | |
assertThat(lhs).isEqualTo(rhs); | |
} | |
@Test | |
public void respectsAssociativityMonalLaw() { | |
Narrowing<String> m = Narrowing.of(ImmutableSet.of("lol", "bal", "zap")); | |
final Function<Set<String>, Narrowing<String>> f = intersectFunction("quux", "lol", "bal"); | |
final Function<Set<String>, Narrowing<String>> g = intersectFunction("lol"); | |
Narrowing<String> lhs = m.flatMap(f).flatMap(g); | |
Narrowing<String> rhs = m.flatMap(new Function<Set<String>, Narrowing<String>>() { | |
@Override | |
public Narrowing<String> apply(Set<String> x) { | |
return f.apply(x).flatMap(g); | |
} | |
}); | |
assertThat(lhs).isEqualTo(rhs); | |
} | |
private static Function<Set<String>, Set<String>> setOfFunction(final String... strs) { | |
return new Function<Set<String>, Set<String>>() { | |
@Override | |
public Set<String> apply(Set<String> ignored) { | |
return ImmutableSet.copyOf(strs); | |
} | |
}; | |
} | |
private static Function<Set<String>, Narrowing<String>> intersectFunction(final String... strs) { | |
return new Function<Set<String>, Narrowing<String>>() { | |
@Override | |
public Narrowing<String> apply(Set<String> found) { | |
return Narrowing.of(Sets.intersection(found, ImmutableSet.copyOf(strs))); | |
} | |
}; | |
} | |
private Function<Set<String>, Narrowing<String>> returnFunction() { | |
return new Function<Set<String>, Narrowing<String>>() { | |
@Override | |
public Narrowing<String> apply(Set<String> found) { | |
return Narrowing.of(found); | |
} | |
}; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment