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

software.amazon.smithy.model.transform.RenameShapes Maven / Gradle / Ivy

/*
 * Copyright 2020 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.transform;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.loader.ModelAssembler;
import software.amazon.smithy.model.node.ArrayNode;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.node.NodeVisitor;
import software.amazon.smithy.model.node.ObjectNode;
import software.amazon.smithy.model.node.StringNode;
import software.amazon.smithy.model.shapes.ModelSerializer;
import software.amazon.smithy.model.shapes.SetShape;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.shapes.ShapeType;
import software.amazon.smithy.model.validation.ValidatedResult;
import software.amazon.smithy.utils.Pair;

/**
 *  Renames shapes using ShapeId pairs while ensuring that the
 *  transformed model is in a consistent state.
 *
 *  

Member shapes are updated when their containing shape is updated. * *

Trait references to ShapeId values are also updated. */ final class RenameShapes { private final Map renamed; private final ModelAssembler assembler; RenameShapes(Map renamed, Supplier modelAssemblerSupplier) { this.renamed = new HashMap<>(renamed); this.assembler = modelAssemblerSupplier.get(); } Model transform(ModelTransformer transformer, Model model) { // Remove any no-op pairs to avoid renaming shapes unnecessarily. renamed.keySet().removeIf(fromId -> fromId.equals(renamed.get(fromId))); if (renamed.isEmpty()) { return model; } // Creates a set that will be used for checking if a string value needs to be renamed or not. Set toRename = renamed.keySet().stream() .map(ShapeId::toString) .collect(Collectors.toSet()); // TODO: this transform serializes the model, then deserializes it. Because of this, if the model // contained sets via loading a 1.0 model, then the set will be serialized in a 2.0 as a list. // To restore them to sets, this track the sets, then change the types after renaming. We should // update this to eventually not need to serialize an intermediate model. Set sets = model.getSetShapes(); // This transformer converts the model into an ObjectNode. This approach was chosen because the // JSON AST format includes fully qualified shape ID values, making it possible rename shapes across // the model by only needing to compare and replace StringNode values. ModelSerializer serializer = ModelSerializer.builder().build(); ObjectNode node = serializer.serialize(model); // Use visitor to traverse node and rebuild model. Node newModel = node.accept(new RenameShapeVisitor(toRename, renamed)); ValidatedResult result = assembler.addDocumentNode(newModel).assemble(); // Transformers shouldn't perform validation ideally. They should only throw errors if the model // can't be transformed. Model modelResult = result.getResult().orElseGet(result::unwrap); return retypeListsBackToSets(transformer, modelResult, sets, renamed); } private Model retypeListsBackToSets( ModelTransformer transformer, Model model, Set sets, Map renamed ) { if (sets.isEmpty()) { return model; } Map retype = new HashMap<>(sets.size()); for (SetShape shape : sets) { ShapeId renamedId = renamed.getOrDefault(shape.getId(), shape.getId()); retype.put(renamedId, ShapeType.SET); } return transformer.changeShapeType(model, retype); } private static final class RenameShapeVisitor extends NodeVisitor.Default { private final Set toRename; private final Map shapeMapping; RenameShapeVisitor(Set toRename, Map shapeMapping) { this.toRename = toRename; this.shapeMapping = shapeMapping; } @Override protected Node getDefault(Node node) { return node; } @Override public Node arrayNode(ArrayNode node) { return node.getElements().stream() .map(element -> element.accept(this)) .collect(ArrayNode.collect()); } @Override public Node objectNode(ObjectNode node) { return node.getMembers().entrySet().stream() .map(entry -> Pair.of(entry.getKey().accept(this), entry.getValue().accept(this))) .collect(ObjectNode.collect(pair -> pair.getLeft().expectStringNode(), Pair::getRight)); } @Override public Node stringNode(StringNode node) { if (toRename.contains(node.getValue())) { ShapeId nodeShapeId = node.expectShapeId(); return new StringNode(shapeMapping.get(nodeShapeId).toString(), node.getSourceLocation()); } return node; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy