Occurrence.java
/*
* SPDX-FileCopyrightText: none
* SPDX-License-Identifier: CC0-1.0
*/
package gov.nist.secauto.metaschema.core.metapath.function;
import gov.nist.secauto.metaschema.core.metapath.ISequence;
import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException;
import gov.nist.secauto.metaschema.core.metapath.item.IItem;
import java.util.Objects;
import edu.umd.cs.findbugs.annotations.NonNull;
/**
* Identifies the occurrence of a sequence used a function argument or return
* value.
*/
public enum Occurrence {
/**
* An empty sequence.
*/
ZERO("", true, Occurrence::handleZero),
/**
* The occurrence indicator {@code "?"}.
*/
ZERO_OR_ONE("?", true, Occurrence::handleZeroOrOne),
/**
* No occurrence indicator.
*/
ONE("", false, Occurrence::handleOne),
/**
* The occurrence indicator {@code "*"}.
*/
ZERO_OR_MORE("*", true, Occurrence::handleZeroOrMore),
/**
* The occurrence indicator {@code "+"}.
*/
ONE_OR_MORE("+", false, Occurrence::handleOneOrMore);
@NonNull
private final String indicator;
private final boolean optional;
@NonNull
private final ISequenceHandler sequenceHandler;
Occurrence(@NonNull String indicator, boolean optional, @NonNull ISequenceHandler sequenceHandler) {
Objects.requireNonNull(indicator, "indicator");
this.indicator = indicator;
this.optional = optional;
this.sequenceHandler = sequenceHandler;
}
/**
* Get the occurrence indicator to use in the signature string for the argument.
*
* @return the occurrence indicator
*/
@NonNull
public String getIndicator() {
return indicator;
}
/**
* Determine if providing a value is optional based on the occurrence.
*
* @return {@code true} if providing a value is optional or {@code false} if
* required
*/
public boolean isOptional() {
return optional;
}
/**
* Get the handler used to check that a sequence meets the occurrence
* requirement.
*
* @return the handler
*/
@NonNull
public ISequenceHandler getSequenceHandler() {
return sequenceHandler;
}
@NonNull
private static <T extends IItem> ISequence<T> handleZero(@NonNull ISequence<T> sequence) {
int size = sequence.size();
if (size != 0) {
throw new InvalidTypeMetapathException(
null,
String.format("an empty sequence expected, but size is '%d'", size));
}
return ISequence.empty();
}
@NonNull
private static <T extends IItem> ISequence<T> handleOne(@NonNull ISequence<T> sequence) {
int size = sequence.size();
if (size != 1) {
throw new InvalidTypeMetapathException(
null,
String.format("a sequence of one expected, but size is '%d'", size));
}
T item = sequence.getFirstItem(true);
return item == null ? ISequence.empty() : ISequence.of(item);
}
@NonNull
private static <T extends IItem> ISequence<T> handleZeroOrOne(@NonNull ISequence<T> sequence) {
int size = sequence.size();
if (size > 1) {
throw new InvalidTypeMetapathException(
null,
String.format("a sequence of zero or one expected, but size is '%d'", size));
}
T item = sequence.getFirstItem(false);
return item == null ? ISequence.empty() : ISequence.of(item);
}
@NonNull
private static <T extends IItem> ISequence<T> handleZeroOrMore(@NonNull ISequence<T> sequence) {
return sequence;
}
@NonNull
private static <T extends IItem> ISequence<T> handleOneOrMore(@NonNull ISequence<T> sequence) {
int size = sequence.size();
if (size < 1) {
throw new InvalidTypeMetapathException(
null,
String.format("a sequence of one or more expected, but size is '%d'", size));
}
return sequence;
}
@FunctionalInterface
public interface ISequenceHandler {
/**
* Check the provided sequence matches the occurrence.
* <p>
* This method may return a new sequence that more efficiently addresses the
* occurrence.
*
* @param <T>
* the sequence item Java type
* @param sequence
* the sequence to check occurrence for
* @return the sequence
* @throws InvalidTypeMetapathException
* if the sequence doesn't match the required occurrence
*/
@NonNull
<T extends IItem> ISequence<T> handle(@NonNull ISequence<T> sequence);
}
}