ResourceAggregator { public List<? extends Resource> excess( List<? extends Resource> sourceResources, List<? extends Resource> resourcesToCompare) { checkSupporting(sourceResources); checkSupporting(resourcesToCompare); final Map<String, Resource> result = sourceResources.stream().collect(Collectors.toMap(Resource::getType, Function.identity())); for (Resource toCompare : resourcesToCompare) { String resourceType = toCompare.getType(); final Resource sourceResource = result.get(resourceType); if (sourceResource != null) { if (sourceResource.getAmount() == toCompare.getAmount()) { result.remove(resourceType); continue; } try { Resource excess = deduct(sourceResource, toCompare); if (excess.getAmount() == 0) { result.remove(resourceType); } else { result.put(resourceType, excess); } } catch (NoEnoughResourcesException e) { result.remove(resourceType); } } } return new ArrayList<>(result.values()); } @Inject  ResourceAggregator(Set<ResourceType> resourcesTypes); Map<String, Resource> aggregateByType(List<? extends Resource> resources); List<? extends Resource> deduct(
      List<? extends Resource> sourceResources, List<? extends Resource> resourcesToDeduct); List<? extends Resource> excess(
      List<? extends Resource> sourceResources, List<? extends Resource> resourcesToCompare); Resource aggregate(Resource resourceA, Resource resourceB); Resource deduct(Resource totalResource, Resource deduction); List<? extends Resource> intersection(
      List<? extends Resource> resourcesA, List<? extends Resource> resourcesB); List<? extends Resource> min(Collection<? extends Resource> resources);  }
@Test public void shouldReturnResourceAsExcessiveWhenToSourceListContainsResourceButToCompareListDoesNotContainItAtAll() throws Exception { final ResourceImpl sourceAResource = new ResourceImpl(A_RESOURCE_TYPE, 5, "unit"); List<? extends Resource> excess = resourceAggregator.excess(singletonList(sourceAResource), emptyList()); assertEquals(excess.size(), 1); assertTrue(excess.contains(sourceAResource)); }
@Test public void shouldNotReturnResourceAsExcessiveWhenToCompareListContainsResourceButSourceDoesNotContainItAtAll() throws Exception { final ResourceImpl toCompareAResource = new ResourceImpl(A_RESOURCE_TYPE, 5, "unit"); List<? extends Resource> excess = resourceAggregator.excess(emptyList(), singletonList(toCompareAResource)); assertTrue(excess.isEmpty()); }
@Test(expectedExceptions = IllegalArgumentException.class) public void shouldThrowIllegalArgumentExceptionWhenFirstListContainsResourceWithUnsupportedTypeOnExcessCalculation() throws Exception { final ResourceImpl dResource = mock(ResourceImpl.class); when(dResource.getType()).thenReturn("resourceD"); resourceAggregator.excess(singletonList(dResource), emptyList()); }
@Test(expectedExceptions = IllegalArgumentException.class) public void shouldThrowIllegalArgumentExceptionWhenSecondListContainsResourceWithUnsupportedTypeExcessCalculation() throws Exception { final ResourceImpl dResource = mock(ResourceImpl.class); when(dResource.getType()).thenReturn("resourceD"); resourceAggregator.excess(emptyList(), singletonList(dResource)); }