Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generators refactoring #3

Merged
merged 5 commits into from
Aug 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
271 changes: 150 additions & 121 deletions core/src/main/java/io/crysknife/client/internal/AbstractBeanManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,56 +19,90 @@
import jakarta.enterprise.inject.Typed;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;

import static io.crysknife.client.internal.QualifierUtil.ANY_ANNOTATION;
import static io.crysknife.client.internal.QualifierUtil.DEFAULT_ANNOTATION;
import static io.crysknife.client.internal.QualifierUtil.DEFAULT_QUALIFIERS;
import static io.crysknife.client.internal.QualifierUtil.SPECIALIZES_ANNOTATION;
import static io.crysknife.client.internal.QualifierUtil.matches;

/**
* @author Dmitrii Tikhomirov Created by treblereel 3/28/19
*/
@SuppressWarnings("unchecked")
public abstract class AbstractBeanManager implements BeanManager {

private final Map<Class, BeanDefinitionHolder> beans = new HashMap<>();

private final Map<Object, BeanFactory> pool = new IdentityHashMap<>();
private final Map<Object, BeanFactory> pool = new HashMap<>();
private final Map<String, Class> beansByBeanName = new HashMap<>();

protected AbstractBeanManager() {
private final Predicate<SyncBeanDefImpl> isTyped =
syncBeanDef -> syncBeanDef.getTyped().isPresent();
private final Predicate<SyncBeanDefImpl> isNotTyped =
syncBeanDef -> syncBeanDef.getTyped().isEmpty();

}
private final Predicate<SyncBeanDefImpl> hasFactory =
syncBeanDef -> syncBeanDef.getFactory().isPresent();

public void register(SyncBeanDefImpl beanDefinition) {
BeanDefinitionHolder holder = get(beanDefinition.getType());
holder.beanDefinition = beanDefinition;
beanDefinition.getAssignableTypes().forEach(superType -> {
get((Class<?>) superType).subTypes.add(holder);
beansByBeanName.put(((Class<?>) superType).getCanonicalName(), (Class<?>) superType);
private final Predicate<SyncBeanDefImpl> hasDefaultQualifiers =
bean -> bean.matches(setOf(DEFAULT_ANNOTATION));

protected AbstractBeanManager() {

});
beansByBeanName.put(beanDefinition.getName(), beanDefinition.getType());
}

private BeanDefinitionHolder get(Class<?> type) {
if (!beans.containsKey(type)) {
BeanDefinitionHolder holder = new BeanDefinitionHolder();
beans.put(type, holder);
public void register(final SyncBeanDefImpl beanDefinition) {
BeanDefinitionHolder holder =
beans.computeIfAbsent(beanDefinition.getType(), k -> new BeanDefinitionHolder());
for (Class<?> superType : (Collection<Class<?>>) beanDefinition.getAssignableTypes()) {
beans.computeIfAbsent(superType, k -> new BeanDefinitionHolder()).subTypes.add(holder);
beansByBeanName.put(superType.getCanonicalName(), superType);
}
return beans.get(type);
Set<Annotation> temp = new HashSet<Annotation>(beanDefinition.getActualQualifiers());
holder.qualifiers.put(temp, beanDefinition);
beansByBeanName.put(beanDefinition.getName(), beanDefinition.getType());
}

@Override
public Collection<SyncBeanDef> lookupBeans(String name) {
if (beansByBeanName.containsKey(name)) {
return lookupBeans(beansByBeanName.get(name));
}
return Collections.EMPTY_SET;
}

public <T> Collection<SyncBeanDef<T>> lookupBeans(final Class<T> type) {
Set<SyncBeanDef<T>> result = new HashSet<>();
if (!beans.containsKey(type)) {
return result;
}
of(beans.get(type)).map(f -> (SyncBeanDef<T>) f).forEach(result::add);
return result;
}

return Collections.EMPTY_SET;
private Stream<SyncBeanDefImpl> of(BeanDefinitionHolder holder,
Predicate<SyncBeanDefImpl>... filters) {
Stream<SyncBeanDefImpl> stream =
Stream.of(holder.subTypes.stream().flatMap(f -> f.qualifiers.values().stream()),
holder.qualifiers.values().stream()).flatMap(Function.identity());
for (Predicate<SyncBeanDefImpl> filter : filters) {
stream = stream.filter(filter);
}

return stream;
}

public <T> Collection<SyncBeanDef<T>> lookupBeans(final Class<T> type, Annotation... qualifiers) {
Expand All @@ -78,33 +112,25 @@ public <T> Collection<SyncBeanDef<T>> lookupBeans(final Class<T> type, Annotatio
}

if (qualifiers.length == 0) {
if (beans.get(type).beanDefinition != null) {
result.add(beans.get(type).beanDefinition);
}
beans.get(type).subTypes.stream().filter(f -> f.beanDefinition != null)
.forEach(bean -> result.add(bean.beanDefinition));
return result;
return lookupBeans(type);
}

if (beans.get(type).beanDefinition != null) {
if (compareAnnotations(beans.get(type).beanDefinition.getActualQualifiers(), qualifiers)) {
result.add(beans.get(type).beanDefinition);
}
}
beans.get(type).subTypes.stream().filter(f -> f.beanDefinition != null)
.filter(f -> compareAnnotations(f.beanDefinition.getActualQualifiers(), qualifiers))
.forEach(bean -> result.add(bean.beanDefinition));
of(beans.get(type)).filter(bean -> bean.matches(setOf(qualifiers))).map(f -> (SyncBeanDef<T>) f)
.forEach(result::add);

return result;
}

private boolean compareAnnotations(Collection<Annotation> all, Annotation... in) {
Annotation[] _all = all.toArray(new Annotation[all.size()]);
return QualifierUtil.matches(in, _all);
}

public <T> SyncBeanDef<T> lookupBean(final Class<T> type) {
return lookupBean(type, QualifierUtil.DEFAULT_ANNOTATION);
Collection<IOCBeanDef<T>> candidates = doLookupBean(type, QualifierUtil.DEFAULT_ANNOTATION);

if (candidates.size() > 1) {
throw BeanManagerUtil.ambiguousResolutionException(type, candidates, DEFAULT_ANNOTATION);
} else if (candidates.isEmpty()) {
throw BeanManagerUtil.unsatisfiedResolutionException(type, DEFAULT_ANNOTATION);
} else {
return (SyncBeanDef<T>) candidates.iterator().next();
}
}

public <T> SyncBeanDef<T> lookupBean(final Class<T> type, Annotation... qualifiers) {
Expand All @@ -126,110 +152,113 @@ public void destroyBean(Object ref) {
}
}

<T> T addBeanInstanceToPool(Object instance, BeanFactory factory) {
pool.put(instance, factory);
return (T) instance;
}
private <T> Collection<IOCBeanDef<T>> doLookupBean(final Class<T> type,
final Annotation... qualifiers) {
if (!beans.containsKey(type)) {
return Collections.EMPTY_SET;
}

<T> Collection<IOCBeanDef<T>> doLookupBean(final Class<T> type, Annotation... qualifiers) {
Collection<IOCBeanDef<T>> candidates = new HashSet<>();
if (beans.containsKey(type)) {
BeanDefinitionHolder holder = beans.get(type);

if (qualifiers == null || qualifiers.length == 0) {
qualifiers = new Annotation[] {QualifierUtil.DEFAULT_ANNOTATION};
}
Optional<IOCBeanDef<T>> maybeTyped = of(holder, isTyped)
.filter(bean -> Arrays.asList(((Typed) bean.getTyped().get()).value()).contains(type))
.map(bean -> (IOCBeanDef<T>) bean).findFirst();

if (beans.get(type).beanDefinition != null) {
if (beans.get(type).beanDefinition.getTyped().isPresent()) {
if (Arrays.stream(((Typed) beans.get(type).beanDefinition.getTyped().get()).value())
.anyMatch(any -> any.equals(type))) {
Set<IOCBeanDef<T>> result = new HashSet<>();
result.add(beans.get(type).beanDefinition);
return result;
}
}
if (maybeTyped.isPresent()) {
return setOf(maybeTyped.get());
}

if (compareAnnotations(beans.get(type).beanDefinition.getQualifiers(), qualifiers)) {
if (beans.get(type).beanDefinition.getFactory().isPresent()) {
candidates.add(beans.get(type).beanDefinition);
}
}
of(holder, hasFactory, isNotTyped).filter(bean -> {
Set<Annotation> temp = new HashSet<>(bean.getActualQualifiers());
Collections.addAll(temp, DEFAULT_QUALIFIERS);
return compareAnnotations(temp, qualifiers);
}).forEach(bean -> candidates.add((IOCBeanDef<T>) bean));

if (qualifiers.length == 1 && isDefault(qualifiers)) {
Optional<IOCBeanDef<T>> maybeSpecialized =
of(holder, isNotTyped).filter(bean -> bean.matches(setOf(SPECIALIZES_ANNOTATION)))
.map(bean -> (IOCBeanDef<T>) bean).findFirst();

// TODO this is not correct, specialized bean totally overrides the parent bean, including
// qualifiers
if (maybeSpecialized.isPresent()) {
return setOf(maybeSpecialized.get());
}

if (qualifiers.length == 1 && !beans.get(type).subTypes.isEmpty() && isDefault(qualifiers)) {
for (BeanDefinitionHolder subType : beans.get(type).subTypes) {
if (subType.beanDefinition != null) {
if (!subType.beanDefinition.getActualQualifiers().isEmpty()
&& compareAnnotations(subType.beanDefinition.getActualQualifiers(),
QualifierUtil.SPECIALIZES_ANNOTATION)) {
Collection<IOCBeanDef<T>> result = new HashSet<>();
result.add(subType.beanDefinition);
return result;
}
}
}
for (BeanDefinitionHolder subType : beans.get(type).subTypes) {
if (subType.beanDefinition != null) {
if (!subType.beanDefinition.getActualQualifiers().isEmpty() && compareAnnotations(
subType.beanDefinition.getActualQualifiers(), QualifierUtil.DEFAULT_ANNOTATION)) {
Collection<IOCBeanDef<T>> result = new HashSet<>();
result.add(subType.beanDefinition);
return result;
}
}
}
for (BeanDefinitionHolder subType : beans.get(type).subTypes) {
Set<Annotation> annotations = new HashSet<>();
annotations.add(QualifierUtil.DEFAULT_ANNOTATION);
if (compareAnnotations(subType.beanDefinition.getActualQualifiers(), qualifiers)) {
if (subType.beanDefinition.getTyped().isPresent()) {
continue;
} else if (subType.beanDefinition.getFactory().isPresent())
candidates.add(subType.beanDefinition);
}
}
} else {
Set<Annotation> _qual = new HashSet<>();
Collections.addAll(_qual, qualifiers);
Collections.addAll(_qual, QualifierUtil.DEFAULT_QUALIFIERS);
_qual.toArray(new Annotation[_qual.size()]);

for (BeanDefinitionHolder subType : beans.get(type).subTypes) {
if (compareAnnotations(subType.beanDefinition.getQualifiers(),
_qual.toArray(new Annotation[_qual.size()]))) {
if (subType.beanDefinition.getTyped().isPresent()
&& !isDefault(subType.beanDefinition.getActualQualifiers())) {
continue;
} else if (subType.beanDefinition.getFactory().isPresent())
candidates.add(subType.beanDefinition);
}
}
Optional<IOCBeanDef<T>> maybeDefault = of(holder, isNotTyped, hasDefaultQualifiers)
.map(bean -> (IOCBeanDef<T>) bean).findFirst();

if (maybeDefault.isPresent()) {
return setOf(maybeDefault.get());
}

of(holder, isNotTyped, hasFactory, hasDefaultQualifiers).map(bean -> (IOCBeanDef<T>) bean)
.forEach(candidates::add);
} else if (qualifiers.length == 1 && isAny(qualifiers)) {

of(holder, hasFactory)
// .filter(bean -> bean.getTyped().isEmpty()) //TODO not sure about this, may I have to
// filter out @typed beans
.map(bean -> (IOCBeanDef<T>) bean).forEach(candidates::add);
} else {
of(holder, isNotTyped, hasFactory).filter(bean -> bean.matches(setOf(qualifiers)))
.map(bean -> (IOCBeanDef<T>) bean).forEach(candidates::add);
}
return candidates;
}

private boolean isDefault(Collection<Annotation> qualifiers) {
if (qualifiers.isEmpty()) {
return false;
}
return isDefault(qualifiers.toArray(new Annotation[qualifiers.size()]));
private boolean compareAnnotations(Collection<Annotation> all, Annotation... in) {
Annotation[] _all = all.toArray(new Annotation[all.size()]);
return matches(in, _all);
}

private boolean isDefault(Annotation[] qualifiers) {
Annotation[] a1 = new Annotation[] {qualifiers[0]};
Annotation[] a2 = new Annotation[] {QualifierUtil.DEFAULT_ANNOTATION};
return QualifierUtil.matches(a1, a2);
Annotation[] a2 = new Annotation[] {DEFAULT_ANNOTATION};
return matches(a1, a2);
}

private boolean isAny(Annotation[] qualifiers) {
Annotation[] a1 = new Annotation[] {qualifiers[0]};
Annotation[] a2 = new Annotation[] {ANY_ANNOTATION};
return matches(a1, a2);
}

private <T> Collection<IOCBeanDef<T>> doLookupBean(final Class<T> type) {
Collection<IOCBeanDef<T>> candidates = new ArrayList<>();
if (beans.containsKey(type)) {
if (!beans.get(type).qualifiers.isEmpty()) {
beans.get(type).qualifiers.values().forEach(candidates::add);
} else {
of(beans.get(type), isNotTyped, hasFactory).filter(syncBeanDef -> {
if (syncBeanDef.getActualQualifiers().isEmpty()) {
return true;
} else {
return syncBeanDef.matches(setOf(DEFAULT_ANNOTATION));
}
}).map(bean -> (IOCBeanDef<T>) bean).forEach(candidates::add);
}
}
return candidates;
}

private boolean compareAnnotations(Annotation[] all, Annotation[] in) {
return QualifierUtil.matches(in, all);
// replace it with setOf right after we move to Java 11 emulated by J2CL
public static <T> Set<T> setOf(T... values) {
Set<T> set = new HashSet<>();
Collections.addAll(set, values);
return Collections.unmodifiableSet(set);
}

<T> T addBeanInstanceToPool(Object instance, BeanFactory<T> factory) {
pool.put(instance, factory);
return (T) instance;
}

private static class BeanDefinitionHolder {

SyncBeanDefImpl beanDefinition;
Set<BeanDefinitionHolder> subTypes = new HashSet<>();
private final Set<BeanDefinitionHolder> subTypes = new HashSet<>();
private final Map<Set<Annotation>, SyncBeanDefImpl> qualifiers = new HashMap<>();

}
}

14 changes: 7 additions & 7 deletions core/src/main/java/io/crysknife/client/internal/BeanFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ protected void setIncompleteInstance(final T instance) {
incompleteInstance = instance;
}

public abstract <T> T getInstance();
public abstract T getInstance();

public void initInstance(T instance) {
if (beanDef.getScope().equals(Dependent.class) || !initialized) {
Expand All @@ -58,19 +58,19 @@ protected void doInitInstance(T instance) {

}

public <T> T createNewInstance() {
public T createNewInstance() {
if (instance != null) {
createInstance();
}
return (T) instance;
return instance;
}

protected <T> T createInstance() {
protected T createInstance() {
throw new UnsupportedOperationException(
"The factory, " + getClass().getSimpleName() + ", only supports contextual instances.");
}

protected <T> T createInstanceInternal() {
protected T createInstanceInternal() {
T instance = createInstance();
return addBeanInstanceToPool(instance, this);
}
Expand All @@ -90,8 +90,8 @@ void onDestroyInternal(T instance) {
initialized = false;
}

protected <T> T addBeanInstanceToPool(Object instance, BeanFactory factory) {
return (T) beanManager.addBeanInstanceToPool(instance, factory);
T addBeanInstanceToPool(T instance, BeanFactory<T> factory) {
return beanManager.addBeanInstanceToPool(instance, factory);
}

}
Loading