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

software.amazon.smithy.model.validation.validators.EnumShapeValidator Maven / Gradle / Ivy

/*
 * Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *   http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

package software.amazon.smithy.model.validation.validators;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;
import software.amazon.smithy.model.FromSourceLocation;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.node.NumberNode;
import software.amazon.smithy.model.shapes.EnumShape;
import software.amazon.smithy.model.shapes.IntEnumShape;
import software.amazon.smithy.model.shapes.MemberShape;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.traits.EnumValueTrait;
import software.amazon.smithy.model.validation.AbstractValidator;
import software.amazon.smithy.model.validation.ValidationEvent;

/**
 * Emits an error validation event if an enum member's enumValue trait has the wrong type,
 * if there are any duplicate values in a single enum, if the enum's default value is
 * set using the enumValue trait, or if an intEnum member lacks an enumValue trait.
 *
 * 

Additionally, emits warning events when enum member names don't follow the recommended * naming convention of all upper case letters separated by underscores. */ public final class EnumShapeValidator extends AbstractValidator { private static final Pattern RECOMMENDED_NAME_PATTERN = Pattern.compile("^[A-Z]+[A-Z_0-9]*$"); @Override public List validate(Model model) { List events = new ArrayList<>(); for (EnumShape shape : model.getEnumShapes()) { validateEnumShape(events, shape); } for (IntEnumShape shape : model.getIntEnumShapes()) { validateIntEnumShape(events, shape); } return events; } private void validateEnumShape(List events, EnumShape shape) { Set values = new HashSet<>(); for (MemberShape member : shape.members()) { EnumValueTrait trait = member.expectTrait(EnumValueTrait.class); Optional value = trait.getStringValue(); if (!value.isPresent()) { events.add(error(member, member.expectTrait(EnumValueTrait.class), "enum members can only be assigned string values, but found: " + Node.printJson(trait.toNode()))); } else { if (!values.add(value.get())) { events.add(error(member, String.format("Multiple enum members found with duplicate value `%s`", value.get()))); } if (value.get().equals("")) { events.add(error(member, "enum values may not be empty.")); } } validateEnumMemberName(events, member); } } private void validateIntEnumShape(List events, IntEnumShape shape) { Set values = new HashSet<>(); for (MemberShape member : shape.members()) { // intEnum must all have the EnumValueTrait. if (!member.hasTrait(EnumValueTrait.ID)) { events.add(missingIntEnumValue(member, member)); continue; } EnumValueTrait trait = member.expectTrait(EnumValueTrait.class); // The EnumValueTrait must point to a number. if (!trait.getIntValue().isPresent()) { ValidationEvent event = error(member, trait, "intEnum members require integer values, but found: " + Node.printJson(trait.toNode())); events.add(event); continue; } NumberNode number = trait.toNode().asNumberNode().get(); // Validate the it is an integer. if (number.isFloatingPointNumber()) { events.add(error(member, trait, "intEnum members do not support floating point values: " + number.getValue())); continue; } long longValue = number.getValue().longValue(); if (longValue > Integer.MAX_VALUE || longValue < Integer.MIN_VALUE) { events.add(error(member, trait, "intEnum members must fit within an integer, but found: " + longValue)); continue; } if (!values.add(number.getValue().intValue())) { events.add(error(member, String.format("Multiple intEnum members found with duplicate value `%d`", number.getValue().intValue()))); } validateEnumMemberName(events, member); } } private ValidationEvent missingIntEnumValue(Shape shape, FromSourceLocation sourceLocation) { return error(shape, sourceLocation, "intEnum members must be assigned an integer value"); } private void validateEnumMemberName(List events, MemberShape member) { if (!RECOMMENDED_NAME_PATTERN.matcher(member.getMemberName()).find()) { events.add(warning(member, String.format( "The name `%s` does not match the recommended enum name format of beginning with an " + "uppercase letter, followed by any number of uppercase letters, numbers, or underscores.", member.getMemberName()))); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy