All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.almworks.jira.structure.api.attribute.AttributeValue Maven / Gradle / Ivy

There is a newer version: 17.25.3
Show newest version
package com.almworks.jira.structure.api.attribute;

import com.almworks.jira.structure.api.error.StructureRuntimeException;
import com.atlassian.annotations.Internal;
import com.atlassian.annotations.PublicApi;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.annotation.concurrent.Immutable;
import java.util.function.Consumer;

/**
 * 

Represents a value, or lack thereof. A defined value must be non-null. Similar to {@code Optional}, * but with additional values to represent special cases and an additional field to carry loader data.

* *

{@code AttributeValue} instances are immutable.

* *

Loader data

* *

Loader data is a field that attribute loaders can use to store additional information, which is not a part of * the resulting value, but which can be used by some other loaders or by the same loader when calculating a multi-row value.

* *

For example, a loader that calculates the sum of all story points under the row without the row itself could store * the own story points value in the loader data, because it will be needed when calculating the attribute value for the parent.

* *

Special values

* *

Special values are provided via the static methods and represent a few special situations. See {@link #undefined()}, {@link #absent()}, * {@link #inaccessible()} and {@link #error()}. Special values may also carry loader data.

* * @param type of the value object * @since 17.0 */ @PublicApi @Immutable public abstract class AttributeValue { private AttributeValue() {} /** * Creates an instance for the given value. * * @param value a value * @param value type * @return attribute value instance */ @NotNull public static AttributeValue of(@NotNull T value) { //noinspection ConstantConditions assert value != null; return ofNullable(value); } /** * Creates an instance for the given value. If the value is {@code null}, returns an undefined value. * * @param value a value or null * @param value type * @return attribute value instance */ @NotNull public static AttributeValue ofNullable(@Nullable T value) { return value == null ? undefined() : new Defined<>(value); } /** * Returns an undefined value. * * @param value type * @return undefined value */ @SuppressWarnings("unchecked") @NotNull public static AttributeValue undefined() { return (AttributeValue) Undefined.BARE_UNDEFINED; } /** * Returns an "error" undefined value. It means there was an error when calculating the value. Specific loaders may put additional information * about the error as loader data. * * @param value type * @return error value */ @SuppressWarnings("unchecked") @NotNull public static AttributeValue error() { return (AttributeValue) Undefined.BARE_ERROR; } /** * Returns an "absent" undefined value, which is used as a placeholder for a value that is being calculated. This method should not be used * outside the Attributes system. * * @param value type * @return absent value */ @SuppressWarnings("unchecked") @Internal @NotNull public static AttributeValue absent() { return (AttributeValue) Undefined.BARE_ABSENT; } /** * Returns an "inaccessible" undefined value, which means the row is not accessible to the user and the real value is not available. * This method should not be used outside the Attributes system. * * @param value type * @return inaccessible value */ @SuppressWarnings("unchecked") @Internal @NotNull public static AttributeValue inaccessible() { return (AttributeValue) Undefined.BARE_INACCESSIBLE; } /** * Creates a new instance of {@code AttributeValue} that has the same value, but a new loader data. * * @param loaderData loader data * @return a new attribute value */ @NotNull public abstract AttributeValue withData(@Nullable Object loaderData); /** * Returns the value or {@code null} if this value is undefined. * * @return value */ @Nullable public abstract T getValue(); /** * Returns a defined value, or throws a runtime exception if the value is not defined. * * @return value * @throws StructureRuntimeException if the value is undefined */ @NotNull public final T getDefinedValue() { T value = getValue(); if (value == null) { throw new StructureRuntimeException("unexpected undefined value"); } return value; } /** * Supplies the value to the consumer, if the value is defined. * * @param consumer consumer of the value, will be supplied with a non-null reference * @return this instance */ @NotNull public abstract AttributeValue ifPresent(@NotNull Consumer consumer); /** * Checks if the value is defined. * * @return {@code true} if the value is defined */ public abstract boolean isDefined(); /** * Checks if this is a special "error" value. * * @return {@code true} if this is an error */ public abstract boolean isError(); /** * Checks if this is a special "absent" value. * * @return {@code true} if this is an absent value */ public abstract boolean isAbsent(); /** * Checks if this is a special "inaccessible" value. * * @return {@code true} if this is an inaccessible value */ public abstract boolean isInaccessible(); /** * Returns the loader data if it matches the provided type. * * @param valueClass expected loader data class * @param expected loader data type * @return loader data or {@code null} if it is missing or of other type */ @Nullable public D getLoaderData(Class valueClass) { return null; } /** * Transforms the value to a different type. * * @param spec attribute spec to take the type from * @param the new type * @return this instance */ @NotNull @Internal public AttributeValue cast(AttributeSpec spec) { T value = getValue(); if (value != null) { if (!spec.getFormat().getValueClass().isInstance(value)) { assert false : this + " " + spec; return null; } } //noinspection unchecked return (AttributeValue) this; } @Immutable private static class Defined extends AttributeValue { @NotNull private final T myValue; private Defined(@NotNull T value) { myValue = value; } @NotNull @Override public AttributeValue withData(@Nullable Object loaderData) { return loaderData == null ? this : new DefinedWithLoaderData<>(myValue, loaderData); } @Override public boolean isDefined() { return true; } public boolean isError() { return false; } public boolean isAbsent() { return false; } public boolean isInaccessible() { return false; } @NotNull @Override public T getValue() { return myValue; } @NotNull @Override public AttributeValue ifPresent(@NotNull Consumer consumer) { consumer.accept(myValue); return this; } @Override public String toString() { return String.valueOf(myValue); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Defined that = (Defined) o; return myValue.equals(that.myValue); } @Override public int hashCode() { return myValue.hashCode(); } } @Immutable private static class DefinedWithLoaderData extends Defined { @NotNull private final Object myLoaderData; public DefinedWithLoaderData(@NotNull T value, @NotNull Object loaderData) { super(value); myLoaderData = loaderData; } @Nullable @Override public D getLoaderData(Class valueClass) { return valueClass.isInstance(myLoaderData) ? valueClass.cast(myLoaderData) : null; } @NotNull @Override public AttributeValue withData(@Nullable Object loaderData) { T value = getValue(); return loaderData == null ? new Defined<>(value) : new DefinedWithLoaderData<>(value, loaderData); } @Override public String toString() { return super.toString() + " (+)"; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; DefinedWithLoaderData that = (DefinedWithLoaderData) o; return myLoaderData.equals(that.myLoaderData); } @Override public int hashCode() { return super.hashCode() * 31 + myLoaderData.hashCode(); } } @Immutable private static class Undefined extends AttributeValue { private static final AttributeValue BARE_UNDEFINED = new Undefined(UndefinedKind.UNDEFINED); private static final AttributeValue BARE_ERROR = new Undefined(UndefinedKind.ERROR); private static final AttributeValue BARE_ABSENT = new Undefined(UndefinedKind.ABSENT); private static final AttributeValue BARE_INACCESSIBLE = new Undefined(UndefinedKind.INACCESSIBLE); @NotNull private final UndefinedKind myKind; public Undefined(@NotNull UndefinedKind kind) { myKind = kind; } @NotNull @Override public AttributeValue withData(@Nullable Object loaderData) { return loaderData == null ? this : new UndefinedWithLoaderData<>(myKind, loaderData); } @Nullable @Override public T getValue() { return null; } @NotNull @Override public AttributeValue ifPresent(@NotNull Consumer consumer) { return this; } @Override public boolean isDefined() { return false; } @Override public boolean isError() { return myKind == UndefinedKind.ERROR; } @Override public boolean isAbsent() { return myKind == UndefinedKind.ABSENT; } @Override public boolean isInaccessible() { return myKind == UndefinedKind.INACCESSIBLE; } @NotNull public UndefinedKind getKind() { return myKind; } @Override public String toString() { switch (myKind) { case ERROR: return ""; case ABSENT: return "<->"; case INACCESSIBLE: return "<#>"; default: return ""; } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Undefined that = (Undefined) o; return myKind == that.myKind; } @Override public int hashCode() { return myKind.hashCode(); } } @Immutable private static class UndefinedWithLoaderData extends Undefined { @NotNull private final Object myLoaderData; public UndefinedWithLoaderData(@NotNull UndefinedKind kind, @NotNull Object loaderData) { super(kind); myLoaderData = loaderData; } @Nullable @Override public D getLoaderData(Class valueClass) { return valueClass.isInstance(myLoaderData) ? valueClass.cast(myLoaderData) : null; } @NotNull @Override public AttributeValue withData(@Nullable Object loaderData) { UndefinedKind kind = getKind(); return loaderData == null ? new Undefined<>(kind) : new UndefinedWithLoaderData<>(kind, loaderData); } @Override public String toString() { return super.toString() + " (+)"; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; UndefinedWithLoaderData that = (UndefinedWithLoaderData) o; return myLoaderData.equals(that.myLoaderData); } @Override public int hashCode() { return super.hashCode() * 31 + myLoaderData.hashCode(); } } private enum UndefinedKind { UNDEFINED, ERROR, ABSENT, INACCESSIBLE } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy