Skip to content

Commit

Permalink
Support for custom components and type filtering in code (root) conta…
Browse files Browse the repository at this point in the history
…iners.
  • Loading branch information
neilcsmith-net committed Jul 8, 2024
1 parent 435616d commit 86e5d29
Show file tree
Hide file tree
Showing 16 changed files with 961 additions and 135 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ protected void addChild(String id, Component child) throws VetoException {
child.hierarchyChanged();
}

protected void recordChildType(Component child, ComponentType type) {
childTypeMap.put(Objects.requireNonNull(child), Objects.requireNonNull(type));
}

protected void notifyChild(Component child) throws VetoException {
child.parentNotify(this);
}
Expand Down Expand Up @@ -245,11 +249,10 @@ protected Call processResponse(Call call) throws Exception {
.flatMap(r -> r.as(Component.class))
.orElseThrow();
Call active = getActiveCall();
addChild(active.args().get(0).toString(), child);
String id = active.args().get(0).toString();
ComponentType type = ComponentType.from(active.args().get(1)).orElse(null);
if (type != null) {
childTypeMap.put(child, type);
}
addChild(id, child);
recordChildType(child, type);
return active.reply();
}
}
Expand Down Expand Up @@ -354,6 +357,14 @@ public static abstract class Delegate extends AbstractContainer {
@Override
protected abstract ComponentAddress getAddress();

/**
* Notify the child of its addition to the container by calling through
* to {@link Component#parentNotify(org.praxislive.core.Container)} with
* the wrapper instance.
*
* @param child child being notified
* @throws VetoException if child vetoes being added
*/
@Override
protected abstract void notifyChild(Component child) throws VetoException;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2023 Neil C Smith.
* Copyright 2024 Neil C Smith.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 3 only, as
Expand Down Expand Up @@ -51,17 +51,21 @@ public class FilteredTypes implements SupportedTypes {
private final Container context;
private final Predicate<ComponentType> filter;
private final Supplier<List<ComponentType>> additional;
private final boolean includeParentAdditional;

private Result result;
private List<ComponentType> addedTypes;
private SupportedTypes delegate;
private Result delegateResult;

private FilteredTypes(Container context,
Predicate<ComponentType> filter,
Supplier<List<ComponentType>> additional) {
Supplier<List<ComponentType>> additional,
boolean includeParentAdditional) {
this.context = context;
this.filter = filter;
this.additional = additional;
this.includeParentAdditional = includeParentAdditional;
}

@Override
Expand All @@ -72,12 +76,12 @@ public Result query() {
.filter(d -> d != this)
.orElseGet(() -> new BaseDelegate(this));
delegateResult = delegate.query();
result = calculateResult(delegateResult);
result = calculateResult(delegate, delegateResult);
} else {
var delRes = delegate.query();
if (delRes != delegateResult) {
delegateResult = delRes;
result = calculateResult(delegateResult);
result = calculateResult(delegate, delegateResult);
}
}
return result;
Expand All @@ -91,24 +95,33 @@ public Result query() {
*/
public void reset() {
result = null;
addedTypes = null;
delegate = null;
delegateResult = null;
}

private Result calculateResult(Result delegateResult) {
private Result calculateResult(SupportedTypes delegate, Result delegateResult) {
if (filter == null && additional == null) {
return delegateResult;
}
List<ComponentType> list = new ArrayList<>(delegateResult.types());
if (!includeParentAdditional && delegate instanceof FilteredTypes ft) {
list.removeAll(ft.addedTypes());
}
if (filter != null) {
list.removeIf(Predicate.not(filter));
}
if (additional != null) {
list.addAll(additional.get());
addedTypes = List.copyOf(additional.get());
list.addAll(addedTypes);
}
return new Result(list);
}

private List<ComponentType> addedTypes() {
return addedTypes == null ? List.of() : addedTypes;
}

/**
* Create a FilteredTypes for the provided context. If no
* {@link SupportedTypes} is found in the parent lookup, then the
Expand All @@ -119,7 +132,7 @@ private Result calculateResult(Result delegateResult) {
* @return instance
*/
public static FilteredTypes create(Container context) {
return create(context, null, null);
return create(context, null, null, true);
}

/**
Expand All @@ -135,7 +148,7 @@ public static FilteredTypes create(Container context) {
*/
public static FilteredTypes create(Container context,
Predicate<ComponentType> filter) {
return create(context, filter, null);
return create(context, filter, null, true);
}

/**
Expand All @@ -154,8 +167,33 @@ public static FilteredTypes create(Container context,
public static FilteredTypes create(Container context,
Predicate<ComponentType> filter,
Supplier<List<ComponentType>> additional) {
return create(context, filter, additional, true);
}

/**
* Create a FilteredTypes for the provided context, additionally filtering
* the available types from the parent by the passed in filter, and adding
* in types from the supplied list.
* <p>
* The boolean flag allows to filter out additional types added by the
* parent, if the parent is also using an instance of FilteredTypes.
* <p>
* If the filter is null then a default filter will be used according to
* root type - see {@link #create(org.praxislive.core.Container)}.
*
* @param context container this will be added to
* @param filter filtering to apply to parent result
* @param additional supplier of a list of additional types
* @param includeParentAdditional whether to include additional types from
* the parent
* @return instance
*/
public static FilteredTypes create(Container context,
Predicate<ComponentType> filter,
Supplier<List<ComponentType>> additional,
boolean includeParentAdditional) {
Objects.requireNonNull(context);
return new FilteredTypes(context, filter, additional);
return new FilteredTypes(context, filter, additional, includeParentAdditional);
}

private static class BaseDelegate implements SupportedTypes {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2023 Neil C Smith.
* Copyright 2024 Neil C Smith.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 3 only, as
Expand All @@ -23,56 +23,96 @@

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import org.praxislive.code.CodeComponentFactoryService;
import org.praxislive.code.CodeDelegate;
import org.praxislive.code.CodeFactory;
import org.praxislive.code.CodeRootFactoryService;
import org.praxislive.core.services.ComponentFactory;
import org.praxislive.core.services.ComponentFactoryProvider;
import org.praxislive.core.ComponentType;
import org.praxislive.core.Lookup;
import org.praxislive.core.OrderedMap;

/**
*
*/
class ComponentRegistry {

private final Map<ComponentType, ComponentFactory> componentCache;
private final OrderedMap<ComponentType, ComponentFactory> componentCache;
private final OrderedMap<Class<? extends CodeDelegate>, CodeFactory.Base<CodeDelegate>> baseCache;

private ComponentRegistry(Map<ComponentType, ComponentFactory> componentCache) {
private ComponentRegistry(OrderedMap<ComponentType, ComponentFactory> componentCache,
OrderedMap<Class<? extends CodeDelegate>, CodeFactory.Base<CodeDelegate>> baseCache) {
this.componentCache = componentCache;
this.baseCache = baseCache;
}

ComponentFactory getComponentFactory(ComponentType type) {
return componentCache.get(type);
}

CodeFactory.Base<CodeDelegate> findSuitableBase(Class<? extends CodeDelegate> cls) {
Class<?> c = cls;
while (c != CodeDelegate.class && c != null) {
CodeFactory.Base<CodeDelegate> base = baseCache.get(c);
if (base != null) {
return base;
}
c = c.getSuperclass();
}
return null;
}

static ComponentRegistry getInstance() {
Map<ComponentType, ComponentFactory> componentCache
Map<ComponentType, ComponentFactory> components
= new LinkedHashMap<>();
Map<Class<? extends CodeDelegate>, CodeFactory.Base<CodeDelegate>> bases
= new LinkedHashMap<>();

Lookup.SYSTEM.findAll(ComponentFactoryProvider.class)
.map(ComponentFactoryProvider::getFactory)
.filter(factory -> factory.componentRedirect()
.filter(factory
-> factory.componentRedirect()
.filter(r -> r.service() == CodeComponentFactoryService.class)
.isPresent())
.forEachOrdered(factory -> {
factory.componentTypes().forEachOrdered(type -> {
componentCache.put(type, factory);
components.put(type, factory);
CodeFactory.Base<CodeDelegate> base = findBase(factory, type);
if (base != null) {
bases.putIfAbsent(base.baseClass(), base);
}
});
}
);

Lookup.SYSTEM.findAll(ComponentFactoryProvider.class)
.map(ComponentFactoryProvider::getFactory)
.filter(factory -> factory.rootRedirect()
.filter(factory
-> factory.rootRedirect()
.filter(r -> r.service() == CodeRootFactoryService.class)
.isPresent())
.forEachOrdered(factory -> {
factory.rootTypes().forEachOrdered(type -> {
componentCache.put(type, factory);
components.put(type, factory);
CodeFactory.Base<CodeDelegate> base = findBase(factory, type);
if (base != null) {
bases.putIfAbsent(base.baseClass(), base);
}
});
}
);

return new ComponentRegistry(componentCache);
return new ComponentRegistry(OrderedMap.copyOf(components), OrderedMap.copyOf(bases));
}

private static CodeFactory.Base<CodeDelegate> findBase(ComponentFactory factory, ComponentType type) {
return Optional.ofNullable(factory.componentData(type))
.flatMap(data -> data.find(CodeFactory.class))
.map(CodeFactory::lookup)
.flatMap(data -> data.find(CodeFactory.Base.class))
.orElse(null);
}

}
Loading

0 comments on commit 86e5d29

Please sign in to comment.