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

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

/*
 * Copyright 2021 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.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.selector.Selector;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.traits.MixinTrait;
import software.amazon.smithy.model.traits.Trait;
import software.amazon.smithy.model.traits.TraitDefinition;
import software.amazon.smithy.model.validation.AbstractValidator;
import software.amazon.smithy.model.validation.ValidationEvent;

/**
 * Validates that traits are only applied to compatible shapes.
 *
 * 

A shape must be present in the return value of a selector in order * for the shape to be considered compatible with the selector. * *

This validator emits ERROR events when a trait is applied to a shape * that is not compatible with the trait selector. */ public final class TraitTargetValidator extends AbstractValidator { private static final Pattern SANITIZE = Pattern.compile("\n\\s*"); @Override public List validate(Model model) { List events = new ArrayList<>(); // Group shapes to validate by duplicate selectors to ensure that the // selector is only performed once. Map> selectorsToTraits = new HashMap<>(); // Only validate trait targets for traits that are actually used. for (ShapeId traitId : model.getAppliedTraits()) { model.getShape(traitId).ifPresent(traitShape -> { // Find all shapes that have the used trait applied to it. Set shapes = model.getShapesWithTrait(traitId); validateMixinsUsedAsTraits(traitShape, shapes, events); traitShape.getTrait(TraitDefinition.class).ifPresent(definition -> { // Short circuit for traits that match everything. if (!definition.getSelector().toString().equals("*")) { selectorsToTraits .computeIfAbsent(definition.getSelector(), selector -> new ArrayList<>()) .add(traitId); } }); }); } for (Map.Entry> entry : selectorsToTraits.entrySet()) { validateTraitTargets(model, events, entry.getKey(), entry.getValue()); } return events; } private void validateMixinsUsedAsTraits(Shape traitShape, Set appliedTo, List events) { if (traitShape.hasTrait(MixinTrait.class)) { for (Shape shape : appliedTo) { events.add(error(shape, String.format( "Trait `%s` is a mixin and cannot be applied to `%s`.", Trait.getIdiomaticTraitName(traitShape.getId()), shape.getId()))); } } } private void validateTraitTargets( Model model, List events, Selector selector, List traits ) { Set matches = selector.select(model); for (ShapeId traitId : traits) { // Find all shapes that have the used trait applied to it. for (Shape shape : model.getShapesWithTrait(traitId)) { // Emit events when a shape is applied to something that didn't match the selector. if (!matches.contains(shape)) { // Strip out newlines with successive spaces. String sanitized = SANITIZE.matcher(selector.toString()).replaceAll(" "); events.add(error(shape, shape.findTrait(traitId).get(), String.format( "Trait `%s` cannot be applied to `%s`. This trait may only be applied " + "to shapes that match the following selector: %s", Trait.getIdiomaticTraitName(traitId), shape.getId(), sanitized))); } } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy