| 1 | package com.fwmotion.threescale.cms.support; | |
| 2 | ||
| 3 | import com.fasterxml.jackson.core.JsonProcessingException; | |
| 4 | import com.fasterxml.jackson.databind.ObjectMapper; | |
| 5 | import com.fwmotion.threescale.cms.exception.ThreescaleCmsApiException; | |
| 6 | import com.fwmotion.threescale.cms.exception.ThreescaleCmsException; | |
| 7 | import com.fwmotion.threescale.cms.exception.ThreescaleCmsUnexpectedPaginationException; | |
| 8 | import com.redhat.threescale.rest.cms.ApiException; | |
| 9 | import com.redhat.threescale.rest.cms.model.Error; | |
| 10 | import com.redhat.threescale.rest.cms.model.ListPaginationMetadata; | |
| 11 | import jakarta.annotation.Nonnull; | |
| 12 | import jakarta.annotation.Nullable; | |
| 13 | import jakarta.validation.constraints.Positive; | |
| 14 | import jakarta.validation.constraints.PositiveOrZero; | |
| 15 | ||
| 16 | import java.util.*; | |
| 17 | import java.util.function.Consumer; | |
| 18 | ||
| 19 | public abstract class AbstractPagedRestApiSpliterator<T> implements Spliterator<T> { | |
| 20 | ||
| 21 | protected static final int DEFAULT_REQUESTED_PAGE_SIZE = 20; | |
| 22 | ||
| 23 | private final int requestedPageSize; | |
| 24 | private final ObjectMapper objectMapper; | |
| 25 | private Iterator<T> currentPageIterator; | |
| 26 | private int currentPageSize; | |
| 27 | private int currentPageNumber; | |
| 28 | private boolean didSplit = false; | |
| 29 | ||
| 30 | protected AbstractPagedRestApiSpliterator(@Positive int requestedPageSize, | |
| 31 | @Nonnull ObjectMapper objectMapper, | |
| 32 | @Nonnull Collection<T> currentPage, | |
| 33 | @PositiveOrZero int currentPageNumber) { | |
| 34 | this.requestedPageSize = requestedPageSize; | |
| 35 | this.objectMapper = objectMapper; | |
| 36 | this.currentPageIterator = currentPage.iterator(); | |
| 37 | this.currentPageSize = currentPage.size(); | |
| 38 | this.currentPageNumber = currentPageNumber; | |
| 39 | } | |
| 40 | ||
| 41 | protected AbstractPagedRestApiSpliterator(@Nonnull Collection<T> currentPage, | |
| 42 | @Nonnull ObjectMapper objectMapper, | |
| 43 | @PositiveOrZero int currentPageNumber) { | |
| 44 | this(DEFAULT_REQUESTED_PAGE_SIZE, objectMapper, currentPage, currentPageNumber); | |
| 45 | } | |
| 46 | ||
| 47 | @Nonnull | |
| 48 | ObjectMapper getObjectMapper() { | |
| 49 |
1
1. getObjectMapper : replaced return value with null for com/fwmotion/threescale/cms/support/AbstractPagedRestApiSpliterator::getObjectMapper → SURVIVED |
return objectMapper; |
| 50 | } | |
| 51 | ||
| 52 | @Nullable | |
| 53 | abstract protected Collection<T> getPage(@PositiveOrZero int pageNumber, | |
| 54 | @Positive int pageSize); | |
| 55 | ||
| 56 | @Nonnull | |
| 57 | abstract protected AbstractPagedRestApiSpliterator<T> doSplit( | |
| 58 | @Positive int requestedPageSize, | |
| 59 | @Nonnull Collection<T> currentPage, | |
| 60 | @PositiveOrZero int currentPageNumber); | |
| 61 | ||
| 62 | @Nonnull | |
| 63 | @Override | |
| 64 | public abstract Comparator<? super T> getComparator(); | |
| 65 | ||
| 66 | @Override | |
| 67 | public boolean tryAdvance(@Nonnull Consumer<? super T> action) { | |
| 68 | // Try to advance to next page if the current one doesn't exist | |
| 69 |
2
1. tryAdvance : removed conditional - replaced equality check with true → KILLED 2. tryAdvance : removed conditional - replaced equality check with false → KILLED |
if (!currentPageIterator.hasNext()) { |
| 70 |
2
1. tryAdvance : removed conditional - replaced equality check with false → KILLED 2. tryAdvance : removed conditional - replaced equality check with true → KILLED |
if (didSplit) { |
| 71 |
1
1. tryAdvance : replaced boolean return with true for com/fwmotion/threescale/cms/support/AbstractPagedRestApiSpliterator::tryAdvance → TIMED_OUT |
return false; |
| 72 | } | |
| 73 | ||
| 74 |
1
1. tryAdvance : Replaced integer addition with subtraction → KILLED |
Collection<T> nextPage = getPage(currentPageNumber + 1, requestedPageSize); |
| 75 | ||
| 76 |
4
1. tryAdvance : removed conditional - replaced equality check with true → SURVIVED 2. tryAdvance : removed conditional - replaced equality check with false → KILLED 3. tryAdvance : removed conditional - replaced equality check with true → KILLED 4. tryAdvance : removed conditional - replaced equality check with false → KILLED |
if (nextPage == null || nextPage.isEmpty()) { |
| 77 | didSplit = true; | |
| 78 |
1
1. tryAdvance : replaced boolean return with true for com/fwmotion/threescale/cms/support/AbstractPagedRestApiSpliterator::tryAdvance → SURVIVED |
return false; |
| 79 | } | |
| 80 | ||
| 81 | currentPageIterator = nextPage.iterator(); | |
| 82 | currentPageSize = nextPage.size(); | |
| 83 |
1
1. tryAdvance : Replaced integer addition with subtraction → KILLED |
currentPageNumber++; |
| 84 | } | |
| 85 | ||
| 86 |
1
1. tryAdvance : removed call to java/util/function/Consumer::accept → KILLED |
action.accept(currentPageIterator.next()); |
| 87 |
1
1. tryAdvance : replaced boolean return with false for com/fwmotion/threescale/cms/support/AbstractPagedRestApiSpliterator::tryAdvance → KILLED |
return true; |
| 88 | } | |
| 89 | ||
| 90 | @Nullable | |
| 91 | @Override | |
| 92 | public AbstractPagedRestApiSpliterator<T> trySplit() { | |
| 93 | // Don't split again | |
| 94 |
2
1. trySplit : removed conditional - replaced equality check with true → SURVIVED 2. trySplit : removed conditional - replaced equality check with false → TIMED_OUT |
if (didSplit) { |
| 95 | return null; | |
| 96 | } | |
| 97 | didSplit = true; | |
| 98 | ||
| 99 | // If at the end of the list, return null | |
| 100 |
8
1. trySplit : removed conditional - replaced equality check with false → SURVIVED 2. trySplit : removed conditional - replaced comparison check with false → NO_COVERAGE 3. trySplit : changed conditional boundary → NO_COVERAGE 4. trySplit : removed conditional - replaced comparison check with true → NO_COVERAGE 5. trySplit : removed conditional - replaced comparison check with false → SURVIVED 6. trySplit : removed conditional - replaced equality check with true → KILLED 7. trySplit : changed conditional boundary → KILLED 8. trySplit : removed conditional - replaced comparison check with true → KILLED |
if (currentPageNumber > 0 && !currentPageIterator.hasNext() && currentPageSize < requestedPageSize) { |
| 101 | return null; | |
| 102 | } | |
| 103 | ||
| 104 | // Get the next page | |
| 105 |
1
1. trySplit : Replaced integer addition with subtraction → KILLED |
Collection<T> nextPage = getPage(currentPageNumber + 1, requestedPageSize); |
| 106 | ||
| 107 |
4
1. trySplit : removed conditional - replaced equality check with true → SURVIVED 2. trySplit : removed conditional - replaced equality check with false → SURVIVED 3. trySplit : removed conditional - replaced equality check with false → KILLED 4. trySplit : removed conditional - replaced equality check with true → KILLED |
if (nextPage == null || nextPage.isEmpty()) { |
| 108 | return null; | |
| 109 | } | |
| 110 | ||
| 111 |
2
1. trySplit : replaced return value with null for com/fwmotion/threescale/cms/support/AbstractPagedRestApiSpliterator::trySplit → KILLED 2. trySplit : Replaced integer addition with subtraction → KILLED |
return doSplit(requestedPageSize, nextPage, currentPageNumber + 1); |
| 112 | } | |
| 113 | ||
| 114 | @PositiveOrZero | |
| 115 | @Override | |
| 116 | public long estimateSize() { | |
| 117 |
1
1. estimateSize : replaced long return with 0 for com/fwmotion/threescale/cms/support/AbstractPagedRestApiSpliterator::estimateSize → SURVIVED |
return Long.MAX_VALUE; |
| 118 | } | |
| 119 | ||
| 120 | protected void validateResultPageSize(@Nonnull String type, | |
| 121 | @PositiveOrZero int pageNumber, | |
| 122 | @Positive int pageSize, | |
| 123 | @Nonnull Collection<T> resultPage, | |
| 124 | @Nullable ListPaginationMetadata paginationMetadata) { | |
| 125 | int currentPage = Optional.ofNullable(paginationMetadata) | |
| 126 | .map(ListPaginationMetadata::getCurrentPage) | |
| 127 | .orElse(pageNumber); | |
| 128 | Optional<Integer> totalPagesOptional = Optional.ofNullable(paginationMetadata) | |
| 129 | .map(ListPaginationMetadata::getTotalPages); | |
| 130 | int totalPages = totalPagesOptional | |
| 131 | .orElse(Integer.MAX_VALUE); | |
| 132 | int perPage = Optional.ofNullable(paginationMetadata) | |
| 133 | .map(ListPaginationMetadata::getPerPage) | |
| 134 | .orElse(pageSize); | |
| 135 | ||
| 136 | int expectedPageSize; | |
| 137 |
3
1. validateResultPageSize : removed conditional - replaced comparison check with false → KILLED 2. validateResultPageSize : changed conditional boundary → KILLED 3. validateResultPageSize : removed conditional - replaced comparison check with true → KILLED |
if (currentPage > totalPages) { |
| 138 | expectedPageSize = 0; | |
| 139 |
4
1. validateResultPageSize : removed conditional - replaced equality check with true → KILLED 2. validateResultPageSize : removed conditional - replaced equality check with false → KILLED 3. validateResultPageSize : removed conditional - replaced equality check with true → KILLED 4. validateResultPageSize : removed conditional - replaced equality check with false → KILLED |
} else if (totalPagesOptional.isEmpty() |
| 140 | || currentPage == totalPages) { | |
| 141 | expectedPageSize = Optional.ofNullable(paginationMetadata) | |
| 142 | .map(ListPaginationMetadata::getTotalEntries) | |
| 143 |
2
1. lambda$validateResultPageSize$0 : Replaced integer modulus with multiplication → KILLED 2. lambda$validateResultPageSize$0 : replaced Integer return value with 0 for com/fwmotion/threescale/cms/support/AbstractPagedRestApiSpliterator::lambda$validateResultPageSize$0 → KILLED |
.map(totalEntries -> totalEntries % perPage) |
| 144 | .orElseGet(resultPage::size); | |
| 145 | } else { | |
| 146 | expectedPageSize = perPage; | |
| 147 | } | |
| 148 | ||
| 149 |
2
1. validateResultPageSize : removed conditional - replaced equality check with true → SURVIVED 2. validateResultPageSize : removed conditional - replaced equality check with false → KILLED |
if (resultPage.size() == expectedPageSize) { |
| 150 | return; | |
| 151 | } | |
| 152 | ||
| 153 | throw new ThreescaleCmsUnexpectedPaginationException(type, | |
| 154 | pageNumber, | |
| 155 | pageSize, | |
| 156 | resultPage.size(), | |
| 157 | expectedPageSize); | |
| 158 | } | |
| 159 | ||
| 160 | protected ThreescaleCmsException handleApiException( | |
| 161 | @Nonnull ApiException e, | |
| 162 | @Nonnull String type, | |
| 163 | @PositiveOrZero int pageNumber, | |
| 164 | @Positive int pageSize | |
| 165 | ) { | |
| 166 | String errorMessage = "Unexpected exception while iterating CMS " + type | |
| 167 | + " page " + pageNumber | |
| 168 | + " (with page size of " + pageSize + ")"; | |
| 169 | ||
| 170 | Error responseError; | |
| 171 | try { | |
| 172 | responseError = objectMapper.readValue(e.getResponseBody(), Error.class); | |
| 173 | } catch (JsonProcessingException ex) { | |
| 174 |
1
1. handleApiException : replaced return value with null for com/fwmotion/threescale/cms/support/AbstractPagedRestApiSpliterator::handleApiException → NO_COVERAGE |
return new ThreescaleCmsApiException(e.getCode(), errorMessage, e); |
| 175 | } | |
| 176 | ||
| 177 |
1
1. handleApiException : replaced return value with null for com/fwmotion/threescale/cms/support/AbstractPagedRestApiSpliterator::handleApiException → NO_COVERAGE |
return new ThreescaleCmsApiException(e.getCode(), |
| 178 | responseError, | |
| 179 | errorMessage, | |
| 180 | e); | |
| 181 | } | |
| 182 | ||
| 183 | } | |
Mutations | ||
| 49 |
1.1 |
|
| 69 |
1.1 2.2 |
|
| 70 |
1.1 2.2 |
|
| 71 |
1.1 |
|
| 74 |
1.1 |
|
| 76 |
1.1 2.2 3.3 4.4 |
|
| 78 |
1.1 |
|
| 83 |
1.1 |
|
| 86 |
1.1 |
|
| 87 |
1.1 |
|
| 94 |
1.1 2.2 |
|
| 100 |
1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 |
|
| 105 |
1.1 |
|
| 107 |
1.1 2.2 3.3 4.4 |
|
| 111 |
1.1 2.2 |
|
| 117 |
1.1 |
|
| 137 |
1.1 2.2 3.3 |
|
| 139 |
1.1 2.2 3.3 4.4 |
|
| 143 |
1.1 2.2 |
|
| 149 |
1.1 2.2 |
|
| 174 |
1.1 |
|
| 177 |
1.1 |