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

com.memority.citadel.shared.api.im.AttributeValue Maven / Gradle / Ivy

Go to download

This artifact provides the API classes that are necessary to implement general configuration Rules on the Memority IM platform.

There is a newer version: 3.43.1
Show newest version
/*
 * Copyright (c) 2016-2023 Memority. All Rights Reserved.
 *
 * This file is part of Memority Citadel API , a Memority project.
 *
 * This file is released under the Memority Public Artifacts End-User License Agreement,
 * see 
 * Unauthorized copying of this file, via any medium is strictly prohibited.
 */
package com.memority.citadel.shared.api.im;

import com.fasterxml.jackson.annotation.*;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.Validate;

import javax.xml.bind.annotation.*;
import java.io.Serializable;
import java.util.*;
import java.util.function.Function;

import static java.util.stream.Collectors.toList;

/**
 * This represents an Attribute Value, either mono-valued or multi-valued.
 * 

* An attribute would typically have at least one value, although it is not mandatory, and as such an * "empty" {@link AttributeValue} may be valid. However, all AttributeValue instances obtained from the API * are guaranteed to contain a value and a call to {@link #hasValue()} may be omitted. *

* Attributes cannot have null values. Any attempt to pass a null value * as one of the Attribute Values will simply result in the value being filtered out. *

* The "multi-valued" state of an {@link AttributeValue} object may be unknown in some cases, which is why * it is encoded in the {@link MultiValuedState} enum. However, and again, all AttributeValue instances obtained from the API * are guaranteed to contain either {@link MultiValuedState#MONO} or {@link MultiValuedState#MULTI}. *

* An AttributeValue serializes in JSON to a simple Object with 2 properties: id and values. * The multi-valued information is lost on purpose and the values property uses the following * semantics: *

    *
  • not present => forbidden
  • *
  • null => forbidden
  • *
  • empty array => the attribute has no value (this is the canonical formalism)
  • *
  • single item => for a mono-valued attribute, this is the only attribute value
  • *
  • multiple items => these are the values of multi-valued attribute
  • *
*/ @EqualsAndHashCode(of = {"id", "values"}) @JsonPropertyOrder({"id", "multiValuedState", "values"}) @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "AttributeValueType") public class AttributeValue implements Serializable { /** * The Attribute multi-value information. In some circumstance we don't know for a fact whether or not * an attribute is multivalued, hence the UNKNOWN state. When in doubt, treat as a multi-valued attribute. */ @XmlType(name = "MultiValuedStateType") public enum MultiValuedState { MONO, MULTI, UNKNOWN; public static MultiValuedState get(boolean multiValued) { return multiValued ? MULTI : MONO; } } /** * The attribute identifier. */ @XmlElement private final String id; /** * The non null list of non null values for the Attribute. * For a mono-valued Attribute, this also holds the value as a unique element.. */ @XmlElementWrapper(name = "values") @XmlElement(name = "value") private final List values; /** * Whether this holds a mono-valued (MONO) or multi-valued (MULTI) attribute value. May also be UNKNOWN. */ private MultiValuedState multiValuedState; /** * The Attribute Value's origin. Can safely be ignored in non IDM context. */ private AttributeOrigin origin = AttributeOrigin.EXTERNAL; // For JAXB protected AttributeValue() { this.id = null; this.values = new ArrayList<>(); } /** * General constructor. Any null value provided in the given values list is filtered out and ignored, thus * ensuring that an Attribute never holds null values. * * @param id the attribute id * @param values the list of values, cannot be null but may be empty * @param multiValuedState whether the attribute is multivalued or not, or unknown */ @JsonCreator public AttributeValue(@JsonProperty("id") String id, @JsonProperty("values") List values, @JsonProperty("multivalued") MultiValuedState multiValuedState) { if (multiValuedState == null) { multiValuedState = MultiValuedState.UNKNOWN; } Validate.notNull(id, "Attribute id must be non null!"); Validate.notNull(values, "Attribute values must be non null"); Validate.validState(multiValuedState == MultiValuedState.MULTI || multiValuedState == MultiValuedState.UNKNOWN || values.size() <= 1, "If not multivalued attribute, list of values must contain a single element or no element at all " + "(no value), attribute id: '" + id + "', values: " + values); this.id = id; this.values = values.stream().filter(Objects::nonNull).collect(toList()); this.multiValuedState = multiValuedState; } /** * Construct a new AttributeValue with the given new values. * * @param values the new values * @return a new AttributeValue */ @SuppressWarnings("WeakerAccess") //API public AttributeValue withValues(List values) { AttributeValue copy = new AttributeValue<>(this.getId(), values, this.getMultiValuedState()); copy.setOrigin(this.getOrigin()); return copy; } /** * Constructs a new AttributeValue by converting the values using the given converter. * * @param converter the converter method for the values * @param the type of values * @return a new AttributeValue */ @SuppressWarnings("WeakerAccess") //API public AttributeValue withConversion(Function converter) { AttributeValue copy = new AttributeValue<>( this.getId(), this.values.stream().map(converter).collect(toList()), this.getMultiValuedState()); copy.setOrigin(this.getOrigin()); return copy; } /** * Construct a new AttributeValue with the given new value. * * @param value the new values * @return a new AttributeValue */ @SuppressWarnings("unused") //API public AttributeValue withValue(V value) { return withValues(Collections.singletonList(value)); } /** * @return this AttributeValue with values that are guaranteed to be distinct. */ public AttributeValue distinctValues() { if (!this.hasValue()) { return this; } if (this.values.size() == 1) { return this; } List distinctValues = this.values.stream().distinct().collect(toList()); return withValues(distinctValues); } /** * @return this AttributeValue with values that are guaranteed to be sorted. */ @SuppressWarnings("unused") //API public AttributeValue sortedValues() { if (!this.hasValue()) { return this; } if (this.values.size() == 1) { return this; } List sortedValues = this.values.stream().sorted().collect(toList()); return withValues(sortedValues); } /** * @return this AttributeValue with values that are guaranteed to be sorted and distinct */ public AttributeValue sortedAndDistinctValues() { if (!this.hasValue()) { return this; } if (this.values.size() == 1) { return this; } List sortedAndDistinctValues = this.values.stream().sorted().distinct().collect(toList()); return withValues(sortedAndDistinctValues); } /** * Factory method to construct a new multi-valued AttributeValue instance. * * @param id the attribute id * @param values the array of values, cannot be null but may be empty * @param the type of held values * @return a new AttributeValue instance */ @SafeVarargs public static AttributeValue multi(String id, T... values) { return multi(id, Arrays.asList(values)); } /** * Factory method to construct a new multi-valued AttributeValue instance. * * @param id the attribute id * @param values the list of values, cannot be null but may be empty, null values are filtered out * @param the type of held values * @return a new AttributeValue instance */ public static AttributeValue multi(String id, List values) { return new AttributeValue<>(id, values, MultiValuedState.MULTI); } /** * Factory method to construct a new mono-valued AttributeValue instance. * * @param id the attribute id * @param value the attribute unique value, if null then it is an empty attribute * @param the type of held value * @return a new AttributeValue instance */ public static AttributeValue mono(String id, T value) { return new AttributeValue<>(id, Collections.singletonList(value), MultiValuedState.MONO); } /** * Factory method to construct an empty AttributeValue instance. This should be used * as a temporary or special case container only, as attributes without any value would * otherwise simply be evicted from the list of an object's attributes. * * @param id the attribute id * @param multiValued whether the attribute is multivalued * @param the type of held value * @return a new AttributeValue instance */ public static AttributeValue empty(String id, boolean multiValued) { return new AttributeValue<>(id, Collections.emptyList(), MultiValuedState.get(multiValued)); } /** * Factory method to construct the correct {@link AttributeValue} (mono or multi) using the given id and * an object value of an undefined type. *

* Object value may be either a standard value object (string, date, boolean, etc.) or a {@link List} of such * value objects. * * @param id the attribute id * @param value the attribute value, either a single value object or a {@link List} of value objects * @param the type of held value * @return a new AttributeValue instance */ @SuppressWarnings("unchecked") public static AttributeValue from(String id, Object value) { if (value instanceof List) { return multi(id, (List) value); } else { return mono(id, (T) value); } } /** * Same as {@link #from(String, List, MultiValuedState)}, but with a definite information about * the multivalued characteristic. * @param id the attribute id * @param values the attribute values * @param multiValued whether the attribute is multivalued * @param the type of held value * @return a new AttributeValue instance */ @SuppressWarnings("unchecked") public static AttributeValue from(String id, List values, boolean multiValued) { Validate.notNull(values, "values cannot be null!"); return new AttributeValue(id, values, MultiValuedState.get(multiValued)); } /** * General purpose Factory method that constructs either a mono or multi-valued {@link AttributeValue} * instance depending on the multiValued parameter. When constructing a mono-valued instance, * the values list must contain at most one element. An empty AttributeValue is constructed * by passing an empty list (which would also be achieved by calling {@link #empty(String, boolean)}). * * @param id the attribute id * @param values the list of values, only one single element for a mono-valued attribute * @param multiValuedState whether or not the attribute is multivalued, or it is unknown * @param the type of held value * @return a new AttributeValue instance */ @SuppressWarnings("unchecked") public static AttributeValue from(String id, List values, MultiValuedState multiValuedState) { Validate.notNull(values, "values cannot be null!"); return new AttributeValue(id, values, multiValuedState); } /** * @return the attribute id value */ @JsonProperty("id") public String getId() { return id; } /** * Return the list of values held by this {@link AttributeValue}. It may be empty to represent an * empty attribute (no value). *

* Note: this method is safe to call for either mono or multi-valued attributes. For mono * valued attributes, the list will have at most one element, that represents the attribute's single * value. *

* Warning: the returned list is not modifiable, this method cannot be * used to mutate the attribute values. * * @return the attribute values, never null. */ @JsonProperty("values") public List getValues() { return Collections.unmodifiableList(values); } /** * Return whether or not this {@link AttributeValue} represents an attribute that has a value * or an attribute that has no value. This needs to be checked before a call to {@link #getValue()} * is performed in order to be absolutely safe (although, when using the API, {@link AttributeValue} instances * are guaranteed to have a value). * * @return true if the attribute has a value, false if it does not (empty) */ public boolean hasValue() { return !this.values.isEmpty(); } /** * Return whether or not this {@link AttributeValue} represents an attribute that has a * non null value. * * @return true if the attribute has a non null value, false if it does not (empty) */ public boolean hasNonNullValue() { if (!hasValue()) { return false; } return this.values.stream().anyMatch(Objects::nonNull); } /** * Return a mono-valued attribute's value. *

* This is a convenience method that can only be called on mono-valued attributes that have * an actual value! In some contexts, this should be checked with {@link #hasValue()} * * @return the attribute's only value (may be null) * @throws NoSuchElementException if the attribute has no value * @throws IllegalStateException if the attribute is multi-valued */ @JsonIgnore public V getValue() { if (isMultiValued()) { throw new IllegalStateException( String.format("Attribute '%s' is multi-valued or its multi-valued state is unknown: #getValue() cannot be called!", getId())); } if (getValues().isEmpty()) { throw new NoSuchElementException(String.format("Attribute '%s' has no value", getId())); } return getValues().get(0); } /** * @return true if the attribute multi-valued characteristic is true or unknown, false otherwise */ @JsonIgnore public boolean isMultiValued() { return this.multiValuedState == MultiValuedState.MULTI || this.multiValuedState == MultiValuedState.UNKNOWN; } /** * Return the state of multi-value knowledge concerning this attribute value. Depending on the context of * the attribute creation, this might be {@link MultiValuedState#UNKNOWN}. When accessing AttributeValue objects * provided through the IDM conf API, however, this information is guaranteed to be known and only * {@link MultiValuedState#MULTI} or {@link MultiValuedState#MONO} are returned. * * @return this object multivalued state */ @JsonProperty("multivalued") //for DB serialization @JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = KnownMultiValuedStateFilter.class) public MultiValuedState getMultiValuedState() { return this.multiValuedState; } public void setMultiValuedState(MultiValuedState val) { this.multiValuedState = val; } @JsonIgnore public AttributeOrigin getOrigin() { return origin; } public AttributeValue setOrigin(AttributeOrigin origin) { this.origin = origin; return this; } /** * Utility method returning the class of the attribute values. Can only work if there is a value. * * @return the value class, or null if it cannot be inferred */ @JsonIgnore public Class getValueClass() { Optional value = getValues().stream().filter(Objects::nonNull).findFirst(); return value.map(Object::getClass).orElse(null); } @Override public String toString() { StringBuilder sb = new StringBuilder(getId()).append("="); if (!hasValue()) { sb.append(""); } else { sb.append(isMultiValued() ? getValues() : getValue()); } return sb.toString(); } /** * Used to only output the multiValuedState when the state is not unknown. */ static class KnownMultiValuedStateFilter { @Override public boolean equals(Object obj) { return obj == null || MultiValuedState.UNKNOWN.equals(obj); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy