ModelToViewConverter.java
package swingtree;
import org.jspecify.annotations.Nullable;
import sprouts.HasId;
import swingtree.api.mvvm.ViewSupplier;
import swingtree.components.JScrollPanels;
import javax.swing.JComponent;
import java.awt.Component;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.function.BiFunction;
final class ModelToViewConverter<M> implements ViewSupplier<M> {
static <M, C extends JComponent> ModelToViewConverter<M> of(
C parent,
ViewSupplier<M> viewSupplier,
BiFunction<M, Exception, JComponent> errorViewCreator
) {
return new ModelToViewConverter<>(parent, viewSupplier, errorViewCreator);
}
private static final Object UNIQUE_VIEW_CACHE_KEY = UUID.randomUUID();
private final WeakReference<JComponent> parentRef;
private final ViewSupplier<M> viewCreator;
private final BiFunction<M, Exception, JComponent> errorViewCreator;
private final List<JComponent> childComponents = new ArrayList<>();
ModelToViewConverter(
JComponent parent,
ViewSupplier<M> viewCreator,
BiFunction<M, Exception, JComponent> errorViewCreator
) {
this.parentRef = new WeakReference<>(Objects.requireNonNull(parent));
this.viewCreator = Objects.requireNonNull(viewCreator);
this.errorViewCreator = Objects.requireNonNull(errorViewCreator);
}
void rememberCurrentViewsForReuse() {
JComponent parent = _subViewParent();
if ( parent != null ) {
for ( int vi = 0; vi < parent.getComponentCount(); vi++ ) {
Component child = parent.getComponent(vi);
if ( child instanceof JComponent ) {
childComponents.add((JComponent) child);
}
}
}
}
void clearCurrentViews() {
childComponents.clear();
}
@Override
public UIForAnySwing<?, ?> createViewFor(M viewModel) throws Exception {
return UI.of(_createViewFor(viewModel));
}
private JComponent _createViewFor(M model) {
try {
JComponent existingView = _findCachedViewIn(model);
if ( existingView != null )
return existingView;
else {
UIForAnySwing<?,?> newView = viewCreator.createViewFor(model);
JComponent viewComponent = newView.get((Class) newView.getType());
viewComponent.putClientProperty(UNIQUE_VIEW_CACHE_KEY, _idFrom(model));
return viewComponent;
}
} catch (Exception e) {
return errorViewCreator.apply(model, e);
}
}
private @Nullable JComponent _subViewParent() {
JComponent parent = this.parentRef.get();
if ( parent instanceof JScrollPanels ) {
JScrollPanels panels = (JScrollPanels) parent;
parent = panels.getContentPanel();
}
return parent;
}
private Object _idFrom( M model ) {
Object id = model;
if ( model instanceof HasId ) {
HasId<?> idModel = (HasId<?>) model;
id = idModel.id();
}
return id;
}
private @Nullable JComponent _findCachedViewIn(M model) throws Exception {
JComponent parent = _subViewParent();
if ( parent != null ) {
Object id = _idFrom(model);
for ( JComponent existingSubView : this.childComponents ) {
Object foundId = existingSubView.getClientProperty(UNIQUE_VIEW_CACHE_KEY);
if ( Objects.equals(id, foundId) ) {
existingSubView.putClientProperty(UNIQUE_VIEW_CACHE_KEY, id);
return existingSubView;
}
}
}
return null;
}
}