Skip to content
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2024 the original author or authors.
* Copyright 2019-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,6 +17,8 @@
package org.dockbox.hartshorn.hsl.condition;

import org.checkerframework.checker.nullness.qual.NonNull;
import org.dockbox.hartshorn.hsl.StandardScriptComponentFactory;
import org.dockbox.hartshorn.hsl.customizer.DefaultScriptStatementsParserCustomizer;
import org.dockbox.hartshorn.inject.InjectionCapableApplication;
import org.dockbox.hartshorn.inject.condition.Condition;
import org.dockbox.hartshorn.inject.condition.ConditionContext;
Expand Down Expand Up @@ -80,8 +82,6 @@ private ConditionResult calculateResult(ConditionContext context, RequiresExpres
return ConditionResult.of(result);
}
catch (ScriptEvaluationError e) {
context.application().environment().exceptionHandler()
.handle("Failed to evaluate expression '%s'".formatted(expression), e);
return ConditionResult.notMatched(e.getMessage());
}
}
Expand All @@ -95,7 +95,21 @@ private ConditionResult calculateResult(ConditionContext context, RequiresExpres
* @return a new runtime
*/
protected ValidateExpressionRuntime createRuntime(ConditionContext context) {
ValidateExpressionRuntime runtime = context.application().defaultProvider().get(ValidateExpressionRuntime.class);
InjectionCapableApplication application = context.application();
ValidateExpressionRuntime runtime;
if (application instanceof ApplicationContext applicationContext) {
runtime = new ValidateExpressionRuntime(
applicationContext,
new StandardScriptComponentFactory(),
new DefaultScriptStatementsParserCustomizer()
);
}
else {
// Less predictable, especially with inject configurations enabled. But if the
// application is not using the standard application context, we cannot make assumptions
// in any case.
runtime = application.defaultProvider().get(ValidateExpressionRuntime.class);
}
return this.enhance(runtime, context);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,20 +63,28 @@ public String toString() {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || this.getClass() != o.getClass()) return false;
SimpleTokenType that = (SimpleTokenType) o;
return this.keyword == that.keyword
&& this.standaloneStatement == that.standaloneStatement
&& this.reserved == that.reserved
&& Objects.equals(this.tokenName, that.tokenName)
&& Objects.equals(this.representation, that.representation)
&& Objects.equals(this.assignsWith, that.assignsWith)
&& Objects.equals(this.defaultLexeme, that.defaultLexeme)
&& Arrays.equals(this.characters, that.characters);
TokenType that = (TokenType) o;
return this.keyword == that.keyword()
&& this.standaloneStatement == that.standaloneStatement()
&& this.reserved == that.reserved()
&& Objects.equals(this.tokenName, that.tokenName())
&& Objects.equals(this.representation, that.representation())
&& Objects.equals(this.assignsWith, that.assignsWith())
&& Objects.equals(this.defaultLexeme, that.defaultLexeme())
&& Arrays.equals(this.characters, that.characters());
}

@Override
public int hashCode() {
int result = Objects.hash(this.tokenName, this.representation, this.keyword, this.standaloneStatement, this.reserved, this.assignsWith, this.defaultLexeme);
int result = Objects.hash(
this.tokenName,
this.representation,
this.keyword,
this.standaloneStatement,
this.reserved,
this.assignsWith,
this.defaultLexeme
);
result = 31 * result + Arrays.hashCode(this.characters);
return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.dockbox.hartshorn.inject.component;

import org.dockbox.hartshorn.inject.ComponentKey;
import org.dockbox.hartshorn.util.option.Option;

import java.util.Collection;
Expand Down Expand Up @@ -46,5 +47,7 @@ public interface ComponentRegistry {
*/
Option<ComponentContainer<?>> container(Class<?> type);

Option<ComponentContainer<?>> container(ComponentKey<?> key);

boolean addCustomContainer(ComponentContainer<?> container);
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public class ManagedComponentProviderStrategy implements ComponentProviderStrate
public <T> ObjectContainer<T> get(ComponentKey<T> componentKey, ComponentRequestContext requestContext, ComponentProviderStrategyChain<T> chain) throws ComponentResolutionException, ApplicationException {
if (chain.application().defaultProvider() instanceof ComponentRegistryAwareComponentProvider componentRegistryAware) {
ComponentRegistry componentRegistry = componentRegistryAware.componentRegistry();
if (componentRegistry.container(componentKey.type()).present()) {
if (componentRegistry.container(componentKey).present()) {
// Only redirect if the request is for another scope than the application scope. If we're already
// providing a component in the application scope, we can continue with the chain as usual.
boolean isScopedRequest = componentKey.scope()
Expand All @@ -56,11 +56,11 @@ public <T> ObjectContainer<T> get(ComponentKey<T> componentKey, ComponentRequest
return !key.equals(applicationScope);
});
if (isScopedRequest) {
// Redirect outside the chain to ensure that the component is resolved with the correct scope.
ComponentKey<T> rescopedKey = componentKey.mutable()
.scope(chain.application().defaultProvider().scope())
.build();

// Redirect outside the chain to ensure that the component is resolved with the correct scope.
T instance = chain.application().defaultProvider().get(rescopedKey);
ComponentObjectContainer<T> container = ComponentObjectContainer.ofSingleton(instance);
container.processed(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,17 +276,13 @@ public String toString() {

@Override
public boolean equals(Object other) {
if(this == other) {
if (other == this) {
return true;
}
if(other == null || this.getClass() != other.getClass()) {
if (!(other instanceof ComponentKey<?> otherKey)) {
return false;
}
ComponentKey<?> otherComponentKey = (ComponentKey<?>) other;
return this.postConstructionAllowed == otherComponentKey.postConstructionAllowed
&& this.type.equals(otherComponentKey.type)
&& Objects.equals(this.qualifier, otherComponentKey.qualifier)
&& Objects.equals(this.scope, otherComponentKey.scope);
return SimpleComponentKeyMatcher.StrictComponentKeyMatcher.INSTANCE.matches(this, otherKey);
}

@Override
Expand Down Expand Up @@ -608,12 +604,39 @@ public Builder<T> postConstructionAllowed(boolean postConstructionAllowed) {
* @param strict whether the lookup for this component should be strict
*
* @return this builder
*
* @see #strict()
* @see #fuzzy()
*/
public Builder<T> strict(boolean strict) {
this.strict = Tristate.valueOf(strict);
return this;
}

/**
* Sets that strict matching should be used for this component key. If strict matching is
* used, the type of the hierarchy has to match this key exactly.
*
* <p>Inverse of {@link #fuzzy()}, for convenience.
*
* @return this builder
*/
public Builder<T> strict() {
return this.strict(true);
}

/**
* Sets that fuzzy matching should be used for this component key. If fuzzy matching is
* used, the type of the hierarchy can be a sub-type of this key.
*
* <p>Inverse of {@link #strict()}, for convenience.
*
* @return this builder
*/
public Builder<T> fuzzy() {
return this.strict(false);
}

/**
* Builds a collector key builder for the component type of this key. The collector key builder can be used to
* create a new key for a collection of components of the given type.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2019-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.dockbox.hartshorn.inject;

/**
* A matcher for component keys, to determine whether two component keys are considered equal for
* a specific purpose or context.
*
* @since 0.7.0
*
* @author Guus Lieben
*/
public interface ComponentKeyMatcher {

/**
* Determines whether the {@code actual} component key is a match for the {@code requested}
* component key. Typically the {@code actual} key is the one registered in a container, while
* the {@code requested} key is the one being looked up.
*
* @param requested the component key being requested
* @param actual the component key being checked for a match
* @return true if the keys match, false otherwise
*/
boolean matches(ComponentKey<?> requested, ComponentKey<?> actual);

/**
* Determines whether the {@code actual} component key view is a match for the {@code requested}
* component key. Typically the {@code actual} key view is the one registered in a container,
* while the {@code requested} key is the one being looked up.
*
* @param requested the component key being requested
* @param actual the component key view being checked for a match
* @return true if the keys match, false otherwise
*/
boolean matches(ComponentKey<?> requested, ComponentKeyView<?> actual);
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public interface InjectorConfiguration {
* Returns whether the injector should be in strict mode. In strict mode, the injector will require
* all {@link BindingHierarchy binding hierarchies} to be resolved using exact matching {@link
* ComponentKey keys}. If strict mode is disabled, the injector will attempt to resolve the hierarchy
* using the most specific key available (loose matching).
* using the most specific key available (fuzzy matching).
*
* @return {@code true} if the injector is in strict mode, {@code false} otherwise
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2024 the original author or authors.
* Copyright 2019-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,9 +17,9 @@
package org.dockbox.hartshorn.inject;

/**
* A 'dumb' object factory, which is typically not aware of any IoC container or other context. It is used to create
* instances of objects without any additional context. This is typically used in early bootstrapping of the application,
* before the IoC container is fully initialized.
* A 'dumb' object factory, which is typically not aware of any IoC container or other context. It
* is used to create instances of objects without any additional context. This is typically used in
* early bootstrapping of the application, before the IoC container is fully initialized.
*
* @since 0.7.0
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2024 the original author or authors.
* Copyright 2019-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,13 +16,13 @@

package org.dockbox.hartshorn.inject;

import java.lang.reflect.InvocationTargetException;

import org.dockbox.hartshorn.util.ApplicationRuntimeException;

import java.lang.reflect.InvocationTargetException;

/**
* Reflection-based implementation of {@link ObjectFactory}. This implementation uses reflection to create instances of
* objects.
* Reflection-based implementation of {@link ObjectFactory}. This implementation uses reflection to
* create instances of objects.
*
* @since 0.7.0
*
Expand All @@ -34,9 +34,12 @@ public class ReflectionObjectFactory implements ObjectFactory {
public <T> T create(Class<T> type) {
try {
return type.getConstructor().newInstance();
}
catch(InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException |
NoSuchMethodException | SecurityException e) {
} catch (InstantiationException
| IllegalAccessException
| IllegalArgumentException
| InvocationTargetException
| NoSuchMethodException
| SecurityException e) {
throw new ApplicationRuntimeException(e);
}
}
Expand Down
Loading