software.amazon.smithy.model.neighbor.UnreferencedTraitDefinitions 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.model.neighbor;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.knowledge.NeighborProviderIndex;
import software.amazon.smithy.model.loader.Prelude;
import software.amazon.smithy.model.shapes.ServiceShape;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.traits.TraitDefinition;
import software.amazon.smithy.utils.FunctionalUtils;
/**
* Finds trait definitions that are not connected to a service shape.
*
* Prelude traits are never considered unreferenced.
*/
public final class UnreferencedTraitDefinitions {
private final Predicate keepFilter;
public UnreferencedTraitDefinitions() {
this(FunctionalUtils.alwaysTrue());
}
/**
* @param keepFilter Predicate that if matched keeps a trait definition from being unreferenced.
*/
public UnreferencedTraitDefinitions(Predicate keepFilter) {
this.keepFilter = keepFilter;
}
public Set compute(Model model) {
Walker walker = new Walker(NeighborProviderIndex.of(model).getProvider());
// Begin with a mutable set of all trait definitions contained in the model
Set unused = model.getShapesWithTrait(TraitDefinition.class).stream()
// Exclude prelude traits -- these are defined by Smithy, not by the model itself
.filter(FunctionalUtils.not(Prelude::isPreludeShape))
.collect(Collectors.toSet());
// Find all traits used directly or indirectly by a service shape and remove
// their definitions from the unused set.
model.shapes(ServiceShape.class)
.flatMap(service -> walker.walkShapes(service).stream())
.distinct()
.map(Shape::getAllTraits)
.flatMap(traits -> traits.keySet().stream())
.distinct()
.flatMap(traitId -> getTraitShapes(model, traitId).stream())
.filter(keepFilter)
.forEach(unused::remove);
return unused;
}
private Collection getTraitShapes(Model model, ShapeId traitId) {
return getTraitShapes(model, traitId, new HashMap<>()).values();
}
private Map getTraitShapes(Model model, ShapeId traitId, Map traitShapes) {
Optional initialTraitShapeOp = model.getShape(traitId);
if (initialTraitShapeOp.isPresent()) {
Shape initialTraitShape = initialTraitShapeOp.get();
traitShapes.put(traitId, initialTraitShape);
for (ShapeId metaTraitId : initialTraitShape.getAllTraits().keySet()) {
if (!metaTraitId.equals(TraitDefinition.ID) && !traitShapes.containsKey(metaTraitId)) {
traitShapes.putAll(getTraitShapes(model, metaTraitId, traitShapes));
}
}
}
return traitShapes;
}
}