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

software.amazon.smithy.linters.AbbreviationNameValidator Maven / Gradle / Ivy

/*
 * Copyright 2019 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.linters;

import static java.lang.String.format;
import static software.amazon.smithy.model.validation.ValidationUtils.splitCamelCaseWord;

import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.node.NodeMapper;
import software.amazon.smithy.model.shapes.MemberShape;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.validation.AbstractValidator;
import software.amazon.smithy.model.validation.ValidationEvent;
import software.amazon.smithy.model.validation.ValidatorService;
import software.amazon.smithy.utils.ListUtils;

/**
 * Emits a validation event if shapes or member names do not use strict
 * camelCasing (e.g., XmlRequest is preferred over XMLRequest).
 */
public final class AbbreviationNameValidator extends AbstractValidator {

    /**
     * AbbreviationName configuration settings.
     */
    public static final class Config {
        private List allowedAbbreviations = Collections.emptyList();

        /**
         * A list of abbreviation strings to permit.
         *
         * @return Returns the allowed abbreviations.
         */
        public List getAllowedAbbreviations() {
            return allowedAbbreviations;
        }

        public void setAllowedAbbreviations(List allowedAbbreviations) {
            this.allowedAbbreviations = ListUtils.copyOf(allowedAbbreviations);
        }
    }

    public static final class Provider extends ValidatorService.Provider {
        public Provider() {
            super(AbbreviationNameValidator.class, configuration -> {
                NodeMapper mapper = new NodeMapper();
                return new AbbreviationNameValidator(mapper.deserialize(configuration, Config.class));
            });
        }
    }

    private Config config;

    private AbbreviationNameValidator(Config config) {
        this.config = config;
    }

    @Override
    public List validate(Model model) {
        return model.shapes()
                .flatMap(shape -> validateShapeName(model, shape))
                .collect(Collectors.toList());
    }

    private Stream validateShapeName(Model model, Shape shape) {
        // Exclude members of enums from AbbreviationName validation,
        // as they're intended to be CAPS_SNAKE.
        if (shape.isMemberShape()) {
            Shape container = model.expectShape(shape.asMemberShape().get().getContainer());
            if (container.isEnumShape() || container.isIntEnumShape()) {
                return Stream.empty();
            }
        }

        String descriptor = shape.isMemberShape() ? "member" : "shape";
        String name = shape.asMemberShape()
                .map(MemberShape::getMemberName)
                .orElseGet(() -> shape.getId().getName());

        String recommendedName = createRecommendedName(name);
        if (recommendedName.equals(name)) {
            return Stream.empty();
        }

        return Stream.of(danger(shape, format(
                "%s name, `%s`, contains invalid abbreviations. Change this %s name to `%s`",
                descriptor, name, descriptor, recommendedName)));
    }

    private String createRecommendedName(String name) {
        // Build up the recommended name which will be compared against the actual name.
        StringBuilder recommendedWordBuilder = new StringBuilder();
        for (String word : splitCamelCaseWord(name)) {
            if (config.getAllowedAbbreviations().contains(word)) {
                // Leave allowed abbreviations as-is in the recommended word.
                recommendedWordBuilder.append(word);
            } else if (!isInvalidWord(word)) {
                recommendedWordBuilder.append(word);
            } else {
                recommendedWordBuilder.append(word.substring(0, 1).toUpperCase(Locale.US));
                recommendedWordBuilder.append(word.substring(1).toLowerCase(Locale.US));
            }
        }

        return recommendedWordBuilder.toString();
    }

    private boolean isInvalidWord(String word) {
        return word.chars().filter(c -> c >= 'A' && c <= 'Z').count() > 1;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy