Skip to content

Instantly share code, notes, and snippets.

@tkareine
Last active August 29, 2015 14:23
Show Gist options
  • Save tkareine/7c7dd466e1d1cf4dc369 to your computer and use it in GitHub Desktop.
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…
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;
}
}
}
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;
}
}
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;
}
}
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());
}
}
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