AbstractComposedIdentity.java
package dev.orne.beans;
/*-
* #%L
* Orne Beans
* %%
* Copyright (C) 2023 Orne Developments
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/lgpl-3.0.html>.
* #L%
*/
import javax.validation.constraints.NotNull;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apiguardian.api.API;
import org.apiguardian.api.API.Status;
/**
* Abstract implementation for {@code Identity} for identities composed
* of a multiple inner values.
*
* @author <a href="mailto:wamphiry@orne.dev">(w) Iker Hernaez</a>
* @version 1.0, 2023-05
* @since 0.5
*/
@API(status=Status.MAINTAINED, since="0.5")
public abstract class AbstractComposedIdentity
extends AbstractIdentity {
/** The serial version UID. */
private static final long serialVersionUID = 1L;
/** Default identity token body parts separator. */
public static final String DEFAULT_SEPARATOR = ",";
/** The default placeholder for null identity token parts. */
public static final String DEFAULT_NULL_PLACEHOLDER = "\0";
/**
* Creates a new instance.
*/
protected AbstractComposedIdentity() {
super();
}
/**
* {@inheritDoc}
*/
@Override
protected String getIdentityTokenBody() {
final String[] parts = getIdentityTokenBodyParts();
if (parts.length == 0) {
return null;
}
final String placeholder = getIdentityTokenBodyPartsNullPlaceholder();
for (int i = 0; i < parts.length; i++) {
if (parts[i] == null) {
parts[i] = placeholder;
}
}
return StringUtils.join(
parts,
getIdentityTokenBodyPartsSeparator());
}
/**
* Returns the identity token body composed from the values of this
* identity. Equal instances must return equal identity token body.
*
* @return The identity token body for this instance
*/
protected abstract @NotNull String[] getIdentityTokenBodyParts();
/**
* Return the identity token body parts separator.
* Must return same value for instances of the same class.
*
* @return The identity token body parts separator.
*/
protected @NotNull String getIdentityTokenBodyPartsSeparator() {
return DEFAULT_SEPARATOR;
}
/**
* Return the placeholder for null identity token parts.
* Must return same value for instances of the same class.
*
* @return The placeholder for null identity token parts.
*/
protected @NotNull String getIdentityTokenBodyPartsNullPlaceholder() {
return DEFAULT_NULL_PLACEHOLDER;
}
/**
* Extracts the {@code String} value of a token generated by
* {@code StringIdentity}.
*
* @param prefix The expected identity token prefix.
* @param token The identity token.
* @return The extracted identity token body parts.
* @throws NullPointerException If any argument is {@code null}
* @throws UnrecognizedIdentityTokenException If the identity token is not
* a valid simple identity token or if it doesn't start with the expected
* prefix.
* @see #extractTokenBodyParts(String, String, String, String)
*/
public static @NotNull String[] extractTokenBodyParts(
final @NotNull String prefix,
final @NotNull String token) {
return extractTokenBodyParts(
prefix,
token,
DEFAULT_SEPARATOR,
DEFAULT_NULL_PLACEHOLDER);
}
/**
* Extracts the {@code String} value of a token generated by
* {@code StringIdentity}.
*
* @param prefix The expected identity token prefix.
* @param token The identity token.
* @param separator The identity token body parts separator.
* @return The extracted identity token body parts.
* @throws NullPointerException If any argument is {@code null}
* @throws UnrecognizedIdentityTokenException If the identity token is not
* a valid simple identity token or if it doesn't start with the expected
* prefix.
* @see #extractTokenBodyParts(String, String, String, String)
*/
public static @NotNull String[] extractTokenBodyParts(
final @NotNull String prefix,
final @NotNull String token,
final @NotNull String separator) {
return extractTokenBodyParts(
prefix,
token,
separator,
DEFAULT_NULL_PLACEHOLDER);
}
/**
* Extracts the {@code String} value of a token generated by
* {@code StringIdentity}.
*
* @param prefix The expected identity token prefix.
* @param token The identity token.
* @param separator The identity token body parts separator.
* @param placeholder The {@code null} identity token body part placeholder.
* @return The extracted identity token body parts.
* @throws NullPointerException If any argument is {@code null}
* @throws UnrecognizedIdentityTokenException If the identity token is not
* a valid simple identity token or if it doesn't start with the expected
* prefix.
*/
public static @NotNull String[] extractTokenBodyParts(
final @NotNull String prefix,
final @NotNull String token,
final @NotNull String separator,
final @NotNull String placeholder) {
Validate.notNull(prefix);
Validate.notNull(token);
Validate.notNull(separator);
Validate.notNull(placeholder);
final String body = IdentityTokenFormatter.parse(prefix, token);
final String[] result;
if (body == null) {
result = new String[0];
} else {
result = body.split(separator);
for (int i = 0; i < result.length; i++) {
if (placeholder.equals(result[i])) {
result[i] = null;
}
}
}
return result;
}
/**
* Extracts the body parts of a token generated by
* {@code AbstractComposedIdentity}.
* <p>
* If the resulting value is {@code null} or the body parts count is not
* the expected one an exception is thrown.
*
* @param prefix The expected identity token prefix.
* @param token The identity token.
* @param expectedParts The expected identity token body parts count.
* @return The extracted identity token body parts.
* @throws NullPointerException If any argument is {@code null}
* @throws UnrecognizedIdentityTokenException If the identity token is not
* a valid identity token, if it doesn't start with the expected prefix,
* if the extracted body is null or the extracted body parts count does not
* match the expected parts count.
* @see #extractTokenBodyParts(String, String)
*/
public static @NotNull String[] extractRequiredTokenBodyParts(
final @NotNull String prefix,
final @NotNull String token,
final int expectedParts) {
return extractRequiredTokenBodyParts(
prefix,
token,
DEFAULT_SEPARATOR,
DEFAULT_NULL_PLACEHOLDER,
expectedParts);
}
/**
* Extracts the body parts of a token generated by
* {@code AbstractComposedIdentity}.
* <p>
* If the resulting value is {@code null} or the body parts count is not
* the expected one an exception is thrown.
*
* @param prefix The expected identity token prefix.
* @param token The identity token.
* @param separator The identity token body parts separator.
* @param expectedParts The expected identity token body parts count.
* @return The extracted identity token body parts.
* @throws NullPointerException If any argument is {@code null}
* @throws UnrecognizedIdentityTokenException If the identity token is not
* a valid identity token, if it doesn't start with the expected prefix,
* if the extracted body is null or the extracted body parts count does not
* match the expected parts count.
* @see #extractTokenBodyParts(String, String, String)
*/
public static @NotNull String[] extractRequiredTokenBodyParts(
final @NotNull String prefix,
final @NotNull String token,
final @NotNull String separator,
final int expectedParts) {
return extractRequiredTokenBodyParts(
prefix,
token,
separator,
DEFAULT_NULL_PLACEHOLDER,
expectedParts);
}
/**
* Extracts the body parts of a token generated by
* {@code AbstractComposedIdentity}.
* <p>
* If the resulting value is {@code null} or the body parts count is not
* the expected one an exception is thrown.
*
* @param prefix The expected identity token prefix.
* @param token The identity token.
* @param separator The identity token body parts separator.
* @param placeholder The {@code null} identity token body part placeholder.
* @param expectedParts The expected identity token body parts count.
* @return The extracted identity token body parts.
* @throws NullPointerException If any argument is {@code null}
* @throws UnrecognizedIdentityTokenException If the identity token is not
* a valid identity token, if it doesn't start with the expected prefix,
* if the extracted body is null or the extracted body parts count does not
* match the expected parts count.
* @see #extractTokenBodyParts(String, String, String)
*/
public static @NotNull String[] extractRequiredTokenBodyParts(
final @NotNull String prefix,
final @NotNull String token,
final @NotNull String separator,
final @NotNull String placeholder,
final int expectedParts) {
final String[] result = extractTokenBodyParts(prefix, token, separator, placeholder);
validateTokenBodyParts(token, result, expectedParts);
return result;
}
/**
* Validates that the specified identity token body parts is not null and
* has the expected number of parts.
*
* @param token The identity token.
* @param parts The extracted identity token body parts.
* @param expectedParts The expected identity token body parts count.
* @throws UnrecognizedIdentityTokenException If the extracted body is
* {@code null} or the extracted body parts count does not match the
* expected parts count.
*/
private static void validateTokenBodyParts(
final @NotNull String token,
final @NotNull String[] parts,
final int expectedParts) {
if (parts.length != expectedParts) {
throw new UnrecognizedIdentityTokenException(
"Unrecognized identity token: " + token);
}
}
}