software.amazon.smithy.linters.ReservedWordsValidator 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 java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.node.NodeMapper;
import software.amazon.smithy.model.selector.Selector;
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.Severity;
import software.amazon.smithy.model.validation.ValidationEvent;
import software.amazon.smithy.model.validation.ValidatorService;
/**
* Emits validation events for a configuration of reserved words.
*
* This validator accepts the following optional configuration options:
*
*
* - reserved: ([object]) A list of reserved word configuration
* objects as follows:
*
* - words: ([string]) A list of words that are
* case-insensitively reserved. Leading and trailing wildcards
* ("*") are supported.
*
- terms: ([string]) A list of word boundary terms to test.
* - selector: (string) Specifies a selector for this
* configuration. Defaults to validating all shapes, including
* member names.
*
- reason: (string) A reason to display for why this set of
* words is reserved.
*
*
*
*/
public final class ReservedWordsValidator extends AbstractValidator {
/**
* ReservedWords validator configuration.
*/
public static final class Config {
private List reserved = Collections.emptyList();
public List getReserved() {
return reserved;
}
/**
* Sets the reserved words to validate.
*
* @param reserved Reserved words to set.
*/
public void setReserved(List reserved) {
this.reserved = reserved;
}
}
/**
* A single reserved words configuration.
*/
public static final class ReservedWords {
private Selector selector = Selector.IDENTITY;
private String reason = "";
private final WildcardMatcher wildcardMatcher = new WildcardMatcher();
private final WordBoundaryMatcher wordMatcher = new WordBoundaryMatcher();
/**
* Sets the list of reserved word definitions.
*
* Each word must be a valid word. The word cannot equal "*", and if present,
* "*", must appear at the start or end of the word.
*
* @param words Words to set.
*/
public void setWords(List words) {
words.forEach(wildcardMatcher::addSearch);
}
/**
* Sets the list of reserved word terms to match based on word boundaries.
*
* @param terms Terms to set.
*/
public void setTerms(List terms) {
terms.forEach(wordMatcher::addSearch);
}
/**
* Sets the selector to use for determining which shapes to validate.
*
* @param selector Selector to set.
*/
public void setSelector(Selector selector) {
this.selector = selector;
}
/**
* Sets the reason for why the words are reserved.
*
* @param reason Reason to set.
*/
public void setReason(String reason) {
this.reason = reason;
}
private void validate(Model model, List events) {
for (Shape shape : selector.select(model)) {
validateShape(shape).ifPresent(events::add);
}
}
private Optional validateShape(Shape shape) {
String name = shape.asMemberShape()
.map(MemberShape::getMemberName)
.orElseGet(() -> shape.getId().getName());
return isReservedWord(name) ? Optional.of(emit(shape, name, reason)) : Optional.empty();
}
/**
* Checks a passed word against the reserved words in this configuration.
*
* @param word A value that may be reserved.
* @return Returns true if the word is reserved by this configuration
*/
private boolean isReservedWord(String word) {
return wildcardMatcher.test(word) || wordMatcher.test(word);
}
private ValidationEvent emit(Shape shape, String word, String reason) {
return ValidationEvent.builder()
.severity(Severity.DANGER)
.id(ValidatorService.determineValidatorName(ReservedWordsValidator.class))
.shape(shape)
.message(format("The word `%s` is reserved. %s", word, reason))
.build();
}
}
public static final class Provider extends ValidatorService.Provider {
public Provider() {
super(ReservedWordsValidator.class, node -> {
NodeMapper mapper = new NodeMapper();
return new ReservedWordsValidator(mapper.deserialize(node, Config.class));
});
}
}
private final Config config;
private ReservedWordsValidator(Config config) {
this.config = config;
}
@Override
public List validate(Model model) {
List events = new ArrayList<>();
for (ReservedWords reserved : config.getReserved()) {
reserved.validate(model, events);
}
return events;
}
}