AbstractCollectionBuilder.java
/*
* SPDX-FileCopyrightText: none
* SPDX-License-Identifier: CC0-1.0
*/
package gov.nist.secauto.metaschema.schemagen.json.impl.builder;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import gov.nist.secauto.metaschema.core.model.IFieldDefinition;
import gov.nist.secauto.metaschema.core.model.IFlagDefinition;
import gov.nist.secauto.metaschema.core.model.IFlagInstance;
import gov.nist.secauto.metaschema.core.model.IGroupable;
import gov.nist.secauto.metaschema.core.model.IModelDefinition;
import gov.nist.secauto.metaschema.core.model.INamedModelInstance;
import gov.nist.secauto.metaschema.core.model.INamedModelInstanceAbsolute;
import gov.nist.secauto.metaschema.core.model.INamedModelInstanceGrouped;
import gov.nist.secauto.metaschema.core.util.CollectionUtil;
import gov.nist.secauto.metaschema.core.util.ObjectUtils;
import gov.nist.secauto.metaschema.schemagen.json.IDataTypeJsonSchema;
import gov.nist.secauto.metaschema.schemagen.json.IDefineableJsonSchema.IKey;
import gov.nist.secauto.metaschema.schemagen.json.IDefinitionJsonSchema;
import gov.nist.secauto.metaschema.schemagen.json.IJsonGenerationState;
import java.util.LinkedList;
import java.util.List;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
public abstract class AbstractCollectionBuilder<T extends AbstractCollectionBuilder<T>>
extends AbstractBuilder<T>
implements IModelInstanceBuilder<T> {
private int minOccurrence = IGroupable.DEFAULT_GROUP_AS_MIN_OCCURS;
private int maxOccurrence = IGroupable.DEFAULT_GROUP_AS_MAX_OCCURS;
@NonNull
private final List<IModelInstanceBuilder.IType> types = new LinkedList<>();
@Override
public T addItemType(INamedModelInstanceAbsolute itemType) {
types.add(new AbsoluteType(itemType));
return thisBuilder();
}
@Override
public T addItemType(INamedModelInstanceGrouped itemType) {
types.add(new GroupedType(itemType));
return thisBuilder();
}
@Override
public List<IType> getTypes() {
return CollectionUtil.unmodifiableList(types);
}
@Override
public T minItems(int min) {
if (min < 0) {
throw new IllegalArgumentException(
String.format("The minimum value '%d' cannot be negative.", min));
}
minOccurrence = min;
return thisBuilder();
}
@Override
public T maxItems(int max) {
if (max < -1 || max == 0) {
throw new IllegalArgumentException(
String.format("The maximum value '%d' must be -1 or a positive value.", max));
}
maxOccurrence = max;
return thisBuilder();
}
@Override
public int getMinOccurrence() {
return minOccurrence;
}
@Override
public int getMaxOccurrence() {
return maxOccurrence;
}
/**
* Generates the type reference(s).
*
* @param object
* the parent object node to add properties to
* @param state
* the generation state
*/
protected void buildInternal(
@NonNull ObjectNode object,
@NonNull IJsonGenerationState state) {
if (types.size() == 1) {
// build the item type reference
types.iterator().next().build(object, state);
} else if (types.size() > 1) {
// build an anyOf of the item type references
ArrayNode anyOf = object.putArray("anyOf");
for (IType type : types) {
type.build(anyOf, state);
}
}
}
@SuppressWarnings("PMD.ShortClassName")
private abstract static class Type<T extends INamedModelInstance> implements IModelInstanceBuilder.IType {
@NonNull
private final T namedModelInstance;
@Nullable
private final IFlagInstance jsonKeyFlag;
@Nullable
private final String discriminatorProperty;
@Nullable
private final String discriminatorValue;
@NonNull
private final IKey key;
@SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW", justification = "Use of final fields")
protected Type(@NonNull T instance) {
this.namedModelInstance = instance;
this.jsonKeyFlag = instance.getEffectiveJsonKey();
if (instance instanceof INamedModelInstanceGrouped) {
INamedModelInstanceGrouped grouped = (INamedModelInstanceGrouped) instance;
this.discriminatorProperty = grouped.getParentContainer().getJsonDiscriminatorProperty();
this.discriminatorValue = grouped.getEffectiveDisciminatorValue();
} else {
this.discriminatorProperty = null;
this.discriminatorValue = null;
}
this.key
= IKey.of(
instance.getDefinition(),
jsonKeyFlag == null ? null : jsonKeyFlag.getName(),
this.discriminatorProperty,
this.discriminatorValue);
}
@NonNull
protected T getNamedModelInstance() {
return namedModelInstance;
}
@Nullable
protected IFlagInstance getJsonKeyFlag() {
return jsonKeyFlag;
}
@Nullable
protected String getJsonKeyFlagName() {
return jsonKeyFlag == null ? null : jsonKeyFlag.getEffectiveName();
}
@Nullable
protected String getDiscriminatorProperty() {
return discriminatorProperty;
}
@Nullable
protected String getDiscriminatorValue() {
return discriminatorValue;
}
@Override
public IDefinitionJsonSchema<IFlagDefinition> getJsonKeyFlagSchema(@NonNull IJsonGenerationState state) {
IFlagInstance jsonKey = getJsonKeyFlag();
return jsonKey == null ? null : state.getSchema(IKey.of(jsonKey.getDefinition()));
}
@Override
public IDataTypeJsonSchema getJsonKeyDataTypeSchema(IJsonGenerationState state) {
IFlagInstance jsonKey = getJsonKeyFlag();
return jsonKey == null ? null : state.getDataTypeSchemaForDefinition(jsonKey.getDefinition());
}
@Override
public IDefinitionJsonSchema<IModelDefinition> getJsonSchema(IJsonGenerationState state) {
return state.getSchema(key);
}
@Override
public void build(
@NonNull ArrayNode anyOf,
@NonNull IJsonGenerationState state) {
build(ObjectUtils.notNull(anyOf.addObject()), state);
}
@Override
public void build(
@NonNull ObjectNode object,
@NonNull IJsonGenerationState state) {
IModelDefinition definition = getNamedModelInstance().getDefinition();
int flagCount = definition.getFlagInstances().size();
if (jsonKeyFlag != null) {
--flagCount;
}
if (flagCount > 0) {
IDefinitionJsonSchema<IModelDefinition> schema = getJsonSchema(state);
schema.generateSchemaOrRef(object, state);
} else if (definition instanceof IFieldDefinition) {
IDataTypeJsonSchema schema = state.getSchema(((IFieldDefinition) definition).getJavaTypeAdapter());
schema.generateSchemaOrRef(object, state);
}
}
}
private static final class AbsoluteType
extends Type<INamedModelInstanceAbsolute> {
private AbsoluteType(@NonNull INamedModelInstanceAbsolute namedModelInstance) {
super(namedModelInstance);
}
}
private static final class GroupedType
extends Type<INamedModelInstanceGrouped> {
private GroupedType(@NonNull INamedModelInstanceGrouped namedModelInstance) {
super(namedModelInstance);
}
}
}