Skip to content

Commit 318014a

Browse files
committed
Add truncatedBeforeFirstParameter
1 parent 3316e76 commit 318014a

File tree

4 files changed

+58
-37
lines changed

4 files changed

+58
-37
lines changed

bosk-core/src/main/java/works/bosk/Bosk.java

Lines changed: 28 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -754,48 +754,41 @@ private <S> void triggerCascade(Reference<S> effectiveScope, @Nullable R priorRo
754754
action.accept(effectiveScope);
755755
}
756756
} else {
757-
try {
758-
// There's at least one parameter that hasn't been bound yet. This means
759-
// we need to locate all the matching objects that may have changed.
760-
// We do so by filling in the first parameter with all possible values that
761-
// could correspond to changed objects and then recursing.
762-
//
763-
Path containerPath = effectiveScope.path().truncatedTo(effectiveScope.path().firstParameterIndex());
764-
Reference<EnumerableByIdentifier<?>> containerRef = rootReference().then(enumerableByIdentifierClass(), containerPath);
765-
EnumerableByIdentifier<?> priorContainer = refValueIfExists(containerRef, priorRoot);
766-
EnumerableByIdentifier<?> newContainer = refValueIfExists(containerRef, newRoot);
757+
// There's at least one parameter that hasn't been bound yet. This means
758+
// we need to locate all the matching objects that may have changed.
759+
// We do so by filling in the first parameter with all possible values that
760+
// could correspond to changed objects and then recursing.
761+
//
762+
Reference<EnumerableByIdentifier<?>> containerRef = effectiveScope.truncatedBeforeFirstParameter();
763+
EnumerableByIdentifier<?> priorContainer = refValueIfExists(containerRef, priorRoot);
764+
EnumerableByIdentifier<?> newContainer = refValueIfExists(containerRef, newRoot);
767765

768-
// TODO: If priorContainer == newContainer, can we stop immediately?
766+
// TODO: If priorContainer == newContainer, can we stop immediately?
769767

770-
// Process any deleted items first. This can allow the hook to free some memory
771-
// that can be used by subsequent hooks.
772-
// We do them in reverse order just because that's likely to be the preferred
773-
// order for cleanup activities.
774-
//
775-
// TODO: Should we actually process the hooks themselves in reverse order for the same reason?
776-
//
777-
if (priorContainer != null) {
778-
List<Identifier> priorIDs = priorContainer.ids();
779-
for (Identifier id : priorIDs.reversed()) {
780-
if (newContainer == null || newContainer.get(id) == null) {
781-
triggerCascade(effectiveScope.boundTo(id), priorRoot, newRoot, action);
782-
}
768+
// Process any deleted items first. This can allow the hook to free some memory
769+
// that can be used by subsequent hooks.
770+
// We do them in reverse order just because that's likely to be the preferred
771+
// order for cleanup activities.
772+
//
773+
// TODO: Should we actually process the hooks themselves in reverse order for the same reason?
774+
//
775+
if (priorContainer != null) {
776+
List<Identifier> priorIDs = priorContainer.ids();
777+
for (Identifier id : priorIDs.reversed()) {
778+
if (newContainer == null || newContainer.get(id) == null) {
779+
triggerCascade(effectiveScope.boundTo(id), priorRoot, newRoot, action);
783780
}
784781
}
782+
}
785783

786-
// Then process updated items
787-
//
788-
if (newContainer != null) {
789-
for (Identifier id : newContainer.ids()) {
790-
if (priorContainer == null || priorContainer.get(id) != newContainer.get(id)) {
791-
triggerCascade(effectiveScope.boundTo(id), priorRoot, newRoot, action);
792-
}
784+
// Then process updated items
785+
//
786+
if (newContainer != null) {
787+
for (Identifier id : newContainer.ids()) {
788+
if (priorContainer == null || priorContainer.get(id) != newContainer.get(id)) {
789+
triggerCascade(effectiveScope.boundTo(id), priorRoot, newRoot, action);
793790
}
794791
}
795-
} catch (InvalidTypeException e) {
796-
// TODO: Add truncation methods to Reference so we can refactor this to create
797-
// the container reference without risking an InvalidTypeException
798-
throw new AssertionError("Parameterized reference must be truncatable at the location of the parameter", e);
799792
}
800793
}
801794
}

bosk-core/src/main/java/works/bosk/Path.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,10 @@ public final String segment(int index) {
255255
public abstract String lastSegment();
256256

257257
public abstract int numParameters();
258+
259+
/**
260+
* @throws IllegalArgumentException if {@link #numParameters()} is 0
261+
*/
258262
public abstract int firstParameterIndex();
259263

260264
@Override

bosk-core/src/main/java/works/bosk/Reference.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,18 @@ default <TT> Reference<TT> truncatedTo(Class<TT> targetClass, int remainingSegme
172172
return root().then(targetClass, path().truncatedTo(remainingSegments));
173173
}
174174

175+
/**
176+
* @throws IllegalArgumentException if this reference has no parameters
177+
*/
178+
@SuppressWarnings({"rawtypes", "unchecked"})
179+
default Reference<EnumerableByIdentifier<?>> truncatedBeforeFirstParameter() {
180+
try {
181+
return (Reference<EnumerableByIdentifier<?>>) (Reference) root().then(EnumerableByIdentifier.class, path().truncatedTo(path().firstParameterIndex()));
182+
} catch (InvalidTypeException e) {
183+
throw new AssertionError("Every parameter must belongs to an EnumerableByIdentifier", e);
184+
}
185+
}
186+
175187
/**
176188
* Returns a {@link Reference} with the {@link Path#lastSegment() last segment} removed.
177189
*

bosk-core/src/test/java/works/bosk/ReferenceTest.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import static org.junit.jupiter.api.Assertions.assertFalse;
1717
import static org.junit.jupiter.api.Assertions.assertNull;
1818
import static org.junit.jupiter.api.Assertions.assertSame;
19+
import static org.junit.jupiter.api.Assertions.assertThrows;
1920
import static org.junit.jupiter.api.Assertions.assertTrue;
2021
import static works.bosk.BoskConfig.simpleDriver;
2122

@@ -198,6 +199,17 @@ void truncatedTo() throws InvalidTypeException {
198199
assertEquals(stringRef, stringRef.truncatedTo(String.class, 3));
199200
}
200201

202+
@Test
203+
void truncatedBeforeFirstParameter() {
204+
Identifier id = Identifier.from("id");
205+
206+
assertEquals(refs.catalog(), refs.anyEntity().truncatedBeforeFirstParameter());
207+
assertEquals(refs.catalog(), refs.anySideTableEntry().truncatedBeforeFirstParameter());
208+
assertEquals(refs.stringSideTable(id), refs.anySideTableEntry(id).truncatedBeforeFirstParameter());
209+
assertThrows(IllegalArgumentException.class, () -> refs.anySideTableEntry(id, id).truncatedBeforeFirstParameter(),
210+
"Should throw when called on a reference with no parameters");
211+
}
212+
201213
@Test
202214
void idAt() {
203215
Identifier parentID = Identifier.from("parent");
@@ -320,6 +332,7 @@ public interface Refs {
320332
@ReferencePath("/someStrings") Reference<StringListValueSubclass> someStrings();
321333
@ReferencePath("/someMappedStrings") Reference<MapValue<String>> someMappedStrings();
322334

335+
@ReferencePath("/entities/-entity-") Reference<TestEntity> anyEntity();
323336
@ReferencePath("/entities/-entity-") Reference<TestEntity> entity(Identifier entity);
324337
@ReferencePath("/entities/-entity-/string") Reference<String> string(Identifier entity);
325338
@ReferencePath("/entities/-entity-/testEnum") Reference<TestEnum> testEnum(Identifier entity);
@@ -344,8 +357,7 @@ public interface Refs {
344357
@ReferencePath("/entities/-entity-/optionals/optionalListing") ListingReference<TestChild> optionalListing(Identifier entity);
345358
@ReferencePath("/entities/-entity-/optionals/optionalSideTable") SideTableReference<TestChild, String> optionalSideTable(Identifier entity);
346359

347-
@ReferencePath("/entities/-entity-/stringSideTable/-child-") Reference<String> sideTableEntry(Identifier entity, Identifier child);
348-
@ReferencePath("/entities/-entity-/stringSideTable/-child-") Reference<String> anySideTableEntry(Identifier entity);
360+
@ReferencePath("/entities/-entity-/stringSideTable/-child-") Reference<String> anySideTableEntry(Identifier... parameters);
349361
@ReferencePath("/entities/-entity-/children/-child-") Reference<TestChild> child(Identifier entity, Identifier child);
350362
@ReferencePath("/entities/-entity-/children/-child-") Reference<TestChild> anyChild(Identifier entity);
351363
}

0 commit comments

Comments
 (0)