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
Expand Up @@ -90,6 +90,7 @@ import org.springframework.core.io.FileSystemResource
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Pageable
import com.netgrif.application.engine.objects.utils.Nullable

import java.time.ZoneId
import java.util.stream.Collectors
Expand Down Expand Up @@ -907,6 +908,10 @@ class ActionDelegate {
return find?.call(ico)
}

<T> Nullable<T> nullable(T value) {
return Nullable.of(value)
}

Object get(String key) { map[key] }

void set(String key, Object value) { map[key] = value }
Expand Down Expand Up @@ -2763,12 +2768,12 @@ class ActionDelegate {
String resolveStoragePath(Case aCase, String fileFieldId, String fileName) {
Optional<Field<?>> storageFieldOptional = aCase.getPetriNet().getField(fileFieldId)
if (storageFieldOptional.isEmpty()) {
throw new IllegalArgumentException("Field with id [%s] does not exist on Petri Net [%s]".formatted(fileFieldId, aCase.getPetriNetId()))
}
throw new IllegalArgumentException("Field with id [%s] does not exist on Petri Net [%s]".formatted(fileFieldId, aCase.getPetriNetId()))
}
Field<?> field = storageFieldOptional.get()
if (!(field instanceof StorageField)) {
throw new IllegalArgumentException("Field with id [%s] is not a StorageField on Petri Net [%s]".formatted(fileFieldId, aCase.getPetriNetId()))
}
throw new IllegalArgumentException("Field with id [%s] is not a StorageField on Petri Net [%s]".formatted(fileFieldId, aCase.getPetriNetId()))
}
StorageField<?> storageField = (StorageField<?>) field
IStorageService storageService = storageResolverService.resolve(storageField.storageType)
return storageService.getPath(aCase.stringId, fileFieldId, fileName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.List;

@Configuration
public class GroovyShellConfiguration {

Expand All @@ -18,6 +20,7 @@ public class GroovyShellConfiguration {
@ConditionalOnMissingBean
public ImportCustomizer importCustomizer() {
ImportCustomizer importCustomizer = new ImportCustomizer();
importCustomizer.addStarImports(getDefaultEngineImports());
importCustomizer.addImports(actionsProperties.getImports().toArray(new String[0]));
importCustomizer.addStarImports(actionsProperties.getStarImports().toArray(new String[0]));
importCustomizer.addStaticStars(actionsProperties.getStaticStarImports().toArray(new String[0]));
Expand All @@ -32,4 +35,14 @@ public CompilerConfiguration compilerConfiguration() {
return configuration;
}


protected String[] getDefaultEngineImports() {
return new String[]{
"com.netgrif.application.engine.objects",
"com.netgrif.application.engine.adapter.spring",
"java.time"
};
}


}
2 changes: 1 addition & 1 deletion application-engine/src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,4 @@ netgrif:
RoleActionsRunner: debug
CaseFieldsExpressionRunner: debug
actions:
imports: com.netgrif.application.engine.objects.workflow.domain.menu.MenuItemBody,com.netgrif.application.engine.objects.petrinet.domain.I18nString,com.netgrif.application.engine.objects.workflow.domain.menu.dashboard.DashboardManagementBody,com.netgrif.application.engine.objects.workflow.domain.menu.dashboard.DashboardItemBody
imports: com.netgrif.application.engine.objects.workflow.domain.menu.MenuItemBody,com.netgrif.application.engine.objects.petrinet.domain.I18nString,com.netgrif.application.engine.objects.workflow.domain.menu.dashboard.DashboardManagementBody,com.netgrif.application.engine.objects.workflow.domain.menu.dashboard.DashboardItemBody,com.netgrif.application.engine.objects.utils.Nullable
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
package com.netgrif.application.engine.objects.utils;

import java.io.Serial;
import java.io.Serializable;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;


/**
* A utility class representing a nullable container for a value of type {@code T}. It provides methods to handle
* optionality in a fluent and functional programming style. This class is inspired by {@link Optional}
* but differs in implementation and naming conventions.
*
* @param <T> The type of the value contained in this {@code Nullable}.
*/
public final class Nullable<T> implements Serializable {

@Serial
private static final long serialVersionUID = 8683452581122892189L;

private final T value;

private Nullable(T value) {
this.value = value;
}

/**
* Returns the value held by this instance.
*
* @return the value contained in this instance, or {@code null} if the instance is empty
*/
public T get() {
return value;
}

Comment on lines +33 to +41
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add Jackson annotations to guarantee JSON round-trip (aligns with PR goal).

Without a bean-style getter (e.g., getValue) Jackson will likely serialize this as {}. Expose the wrapped value via @jsonvalue and enable delegating construction via @JsonCreator.

Apply:

 import java.io.Serial;
 import java.io.Serializable;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
@@
-    public T get() {
+    @JsonValue
+    public T get() {
         return value;
     }
@@
-    public static <T> Nullable<T> of(T value) {
+    @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
+    public static <T> Nullable<T> of(T value) {
         return new Nullable<>(value);
     }

Also applies to: 48-51

🤖 Prompt for AI Agents
In
nae-object-library/src/main/java/com/netgrif/application/engine/objects/utils/Nullable.java
around lines 33-41 (and also apply to lines 48-51), the wrapped value isn't
exposed for Jackson so instances serialize as {} — annotate the getter with
@JsonValue to serialize the contained value, and add @JsonCreator (use
Mode.DELEGATING) to the constructor or factory so Jackson can deserialize
directly into the wrapper; add the necessary imports for
com.fasterxml.jackson.annotation.JsonValue and
com.fasterxml.jackson.annotation.JsonCreator and apply the annotations to the
existing get() and constructor/factory accordingly.

/**
* Creates a new {@code Nullable} instance containing the given value.
*
* @param value the value to wrap in a {@code Nullable} instance, can be {@code null}
* @return a {@code Nullable} instance wrapping the provided value
*/
public static <T> Nullable<T> of(T value) {
return new Nullable<>(value);
}

/**
* Returns an empty {@code Nullable} instance holding no value.
*
* @param <T> the type of the value that can be held by this {@code Nullable} instance
* @return an empty {@code Nullable} instance
*/
public static <T> Nullable<T> empty() {
return new Nullable<>(null);
}

/**
* Checks if a value is present in this instance.
*
* @return {@code true} if the value is not {@code null}, otherwise {@code false}
*/
public boolean isPresent() {
return value != null;
}

/**
* Checks if this instance holds no value.
*
* @return {@code true} if the value is {@code null}, otherwise {@code false}
*/
public boolean isEmpty() {
return value == null;
}

/**
* If a value is present in this instance, performs the given action with the value.
*
* @param action the action to be performed if a value is present; must be non-null
*/
public void ifPresent(Consumer<? super T> action) {
if (value != null) {
action.accept(value);
}
}

/**
* Performs the given action with the value if it is present, otherwise executes the provided empty action.
*
* @param action the {@code Consumer} to be executed if the value is present; must not be null
* @param emptyAction the {@code Runnable} to be executed if the value is not present; must not be null
*/
public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) {
if (value != null) {
action.accept(value);
} else {
emptyAction.run();
}
}

/**
* Converts the value held by this instance into an {@code Optional}.
*
* @return an {@code Optional} containing the value if it is present, or an empty {@code Optional} if the value is {@code null}
*/
public Optional<T> toOptional() {
return Optional.ofNullable(value);
}

/**
* Filters the value contained in this {@code Nullable} instance based on the provided predicate.
* If the value is present and satisfies the predicate, this instance is returned.
* If the value does not satisfy the predicate or if the instance is empty, an empty {@code Nullable} is returned.
*
* @param predicate the predicate used to evaluate the contained value; must be non-null
* @return this {@code Nullable} instance if the value satisfies the predicate,
* or an empty {@code Nullable} instance otherwise
*/
public Nullable<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (isEmpty()) {
return this;
} else {
return predicate.test(value) ? this : empty();
}
}

/**
* Transforms the value contained in this {@code Nullable} instance using the provided mapping function.
* If this {@code Nullable} instance is empty, an empty {@code Nullable} instance is returned.
*
* @param <U> the type of the value produced by the mapping function
* @param mapper the function to apply to the value; must not be null
* @return a {@code Nullable} instance containing the value produced by applying the mapping function,
* or an empty {@code Nullable} instance if this instance is empty
*/
public <U> Nullable<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (isEmpty()) {
return empty();
} else {
return Nullable.of(mapper.apply(value));
}
}

/**
* Applies the provided mapping function to the value contained in this {@code Nullable} instance,
* and returns the {@code Nullable} instance produced by the mapping function. If this instance is empty,
* an empty {@code Nullable} is returned.
*
* @param <U> the type of the value contained in the resulting {@code Nullable} instance
* @param mapper the mapping function to apply to the value if it is present; must not be null
* @return a {@code Nullable} instance produced by applying the mapping function to the value,
* or an empty {@code Nullable} if this instance is empty
*/
public <U> Nullable<U> flatMap(Function<? super T, ? extends Nullable<? extends U>> mapper) {
Objects.requireNonNull(mapper);
if (isEmpty()) {
return empty();
} else {
@SuppressWarnings("unchecked")
Nullable<U> r = (Nullable<U>) mapper.apply(value);
return Objects.requireNonNull(r);
}
}

/**
* Returns this {@code Nullable} instance if a value is present, otherwise returns the result
* of invoking the provided {@code Supplier}.
*
* @param supplier the {@code Supplier} providing an alternative {@code Nullable} instance
* if this instance is empty; must not be {@code null}
* @return this {@code Nullable} instance if it contains a value, or the {@code Nullable} instance
* provided by the supplier if this instance is empty
* @throws NullPointerException if the supplier is {@code null} or the {@code Nullable} instance
* provided by the supplier is {@code null}
*/
public Nullable<T> or(Supplier<? extends Nullable<? extends T>> supplier) {
Objects.requireNonNull(supplier);
if (isPresent()) {
return this;
} else {
@SuppressWarnings("unchecked")
Nullable<T> r = (Nullable<T>) supplier.get();
return Objects.requireNonNull(r);
}
}

/**
* Creates a sequential {@code Stream} containing the value held by this {@code Nullable} instance,
* if a value is present. If this instance is empty, returns an empty {@code Stream}.
*
* @return a {@code Stream} containing the value if it is present, or an empty {@code Stream} otherwise
*/
public Stream<T> stream() {
if (isEmpty()) {
return Stream.empty();
} else {
return Stream.of(value);
}
}

/**
* Returns the value held by this instance if it is non-null; otherwise, returns the specified default value.
*
* @param other the value to be returned if the current value is null
* @return the value held by this instance if it is non-null, or the specified default value if the value is null
*/
public T orElse(T other) {
return value != null ? value : other;
}

/**
* Returns the value held by this instance if it is present; otherwise,
* returns the result produced by the provided {@code Supplier}.
*
* @param supplier a {@code Supplier} to provide an alternative value
* if the current instance holds no value; must not be {@code null}
* @return the value contained in this instance if present, or the value
* obtained from the supplied {@code Supplier} if the instance is empty
* @throws NullPointerException if the provided supplier is {@code null}
*/
public T orElseGet(Supplier<? extends T> supplier) {
return value != null ? value : supplier.get();
}

/**
* Returns the value held by this instance if it is present; otherwise,
* throws a {@link NoSuchElementException}.
*
* @return the value contained in this instance
* @throws NoSuchElementException if no value is present in this instance
*/
public T orElseThrow() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}

/**
* Returns the value held by this instance if it is present, otherwise throws an exception
* supplied by the provided {@code Supplier}.
*
* @param <X> the type of the exception to be thrown
* @param exceptionSupplier the supplier that provides the exception to be thrown if no value is present; must not be null
* @return the value held by this instance if it is present
* @throws X if no value is present
* @throws NullPointerException if the {@code exceptionSupplier} is null
*/
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}

/**
* Compares this {@code Nullable} instance with the specified object for equality.
* Two {@code Nullable} instances are considered equal if they both contain the
* same value or are both empty.
*
* @param obj the object to be compared for equality with this {@code Nullable} instance
* @return {@code true} if the specified object is equal to this {@code Nullable} instance,
* {@code false} otherwise
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}

return obj instanceof Nullable<?> other
&& Objects.equals(value, other.value);
}

/**
* Computes the hash code for this instance based on its value field.
*
* @return the hash code as an integer for this instance,
* calculated using the value field or default if the field is null.
*/
@Override
public int hashCode() {
return Objects.hashCode(value);
}

/**
* Returns a string representation of the object.
* If the encapsulated value is non-null, the string will include the value.
* If the encapsulated value is null, a representation indicating emptiness will be returned.
*
* @return a string representation of the object, indicating the encapsulated value or an empty state
*/
@Override
public String toString() {
return value != null ? value.toString() : "";
}

}
Loading