LensCore.java
package sprouts.impl;
import org.jspecify.annotations.Nullable;
import sprouts.Channel;
import sprouts.Val;
import java.util.List;
/**
* Abstracts the source-interaction behavior of a property which derives its item from
* one or more other properties. A {@link PropertyLens} delegates all source-specific
* logic to a {@code LensCore}: fetching the current item from the source(s), writing a
* new item back, and telling the property which sources to observe.
* <p>
* Implementations include:
* <ul>
* <li>{@link SingleLensCore} — a single parent property with a {@link sprouts.Lens}</li>
* <li>{@link DualLensCore} — two parent properties with a getter/setter pair</li>
* <li>{@link ParamLensCore} — a parent property combined with an external parameterized getter/setter pair</li>
* <li>{@link CompositeCore} — a read-only fold over an arbitrary number of joined properties</li>
* </ul>
*
* @param <T> The item type of the derived property.
*/
interface LensCore<T extends @Nullable Object> {
/**
* Fetches the current item from the parent source(s).
* On error, the implementation should return {@code lastKnownItem} as a fallback,
* so that a derived property never exposes a half computed or illegal item.
*
* @param lastKnownItem The last known item, used as fallback if fetching fails.
* @param logDegradation Whether falling back to {@code lastKnownItem} should be logged.
* This is {@code true} on the event-propagation path, where the
* anomaly first occurs, and {@code false} on the ordinary read path,
* which would otherwise report the very same problem over and over
* again for as long as the sources stay in their broken state.
* @return The fetched item, or {@code lastKnownItem} on error.
*/
@Nullable T fetchFromSources(@Nullable T lastKnownItem, boolean logDegradation);
/**
* Writes a new item back to the parent source(s).
*
* @param channel The channel to use for the write.
* @param newItem The new item to write.
* @throws UnsupportedOperationException If this core is read-only, see {@link #isView()}.
*/
void writeToSources(Channel channel, @Nullable T newItem);
/**
* Returns the parent source properties that this core observes.
* The {@link PropertyLens} subscribes to each of these with weak listeners,
* which is why immutable sources should be left out: their item can never change,
* so observing them would only produce listeners that are never called.
*
* @return An {@link Iterable} over all observed parent source properties.
*/
Iterable<? extends Val<?>> sources();
/**
* Whether the property backed by this core is a read-only view instead of a lens.
* This determines what {@link sprouts.Val#isView()} and {@link sprouts.Val#isLens()}
* report for the derived property. A core which returns {@code true} here is not
* required to support {@link #writeToSources(Channel, Object)}.
*
* @return {@code true} if the derived property should present itself as a view.
*/
default boolean isView() {
return false;
}
/**
* Whether a source-change callback should be suppressed.
* Used by the dual lens core to avoid re-entrant notifications
* while writing back to both parents during a {@code set()} call.
*
* @return {@code true} if the callback should be suppressed.
*/
boolean shouldSuppressSourceCallback();
/**
* A short display name for this core type, used in {@code toString()}.
* For example, {@code "Lens"} or {@code "DualLens"}.
*
* @return The core name.
*/
String coreName();
/**
* Creates a fresh instance with the same configuration but independent mutable state.
* Used by {@link PropertyLens#withId(String)} so that each lens instance has its own
* re-entrancy guard and listener registrations.
*
* @return A new {@code LensCore} with the same parent/getter/setter references.
*/
LensCore<T> newInstance();
}