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

software.amazon.smithy.diff.Differences Maven / Gradle / Ivy

Go to download

This module detects differences between two Smithy models, identifying changes that are safe and changes that are backward incompatible.

The newest version!
/*
 * 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.diff;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.utils.Pair;

/**
 * Queryable container for detected structural differences between two models.
 */
public final class Differences {
    private final Model oldModel;
    private final Model newModel;
    private final List> changedShapes = new ArrayList<>();
    private final List changedMetadata = new ArrayList<>();

    private Differences(Model oldModel, Model newModel) {
        this.oldModel = oldModel;
        this.newModel = newModel;
        detectMetadataChanges(oldModel, newModel, this);
        detectShapeChanges(oldModel, newModel, this);
    }

    static Differences detect(Model oldModel, Model newModel) {
        return new Differences(oldModel, newModel);
    }

    /**
     * Gets the old model.
     *
     * @return Returns the old model.
     */
    public Model getOldModel() {
        return oldModel;
    }

    /**
     * Gets the new model.
     *
     * @return Returns the new model.
     */
    public Model getNewModel() {
        return newModel;
    }

    /**
     * Gets all added shapes.
     *
     * @return Returns a stream of each added shape.
     */
    public Stream addedShapes() {
        return newModel.shapes().filter(shape -> !oldModel.getShape(shape.getId()).isPresent());
    }

    /**
     * Gets all of the added shapes of a specific type.
     *
     * @param shapeType Type of shape to find.
     * @param  Type of shape.
     * @return Returns a stream of each added shape of a specific type.
     */
    public  Stream addedShapes(Class shapeType) {
        return addedShapes().filter(shapeType::isInstance).map(shapeType::cast);
    }

    /**
     * Gets all added metadata.
     *
     * 

Each Pair returned contains the name of the metadata key on * the left of the Pair and the metadata value on the right. * * @return Returns a stream of added metadata. */ public Stream> addedMetadata() { return newModel.getMetadata().entrySet().stream() .filter(entry -> !oldModel.getMetadata().containsKey(entry.getKey())) .map(entry -> Pair.of(entry.getKey(), entry.getValue())); } /** * Gets all removed shapes. * * @return Returns a stream of each removed shape. */ public Stream removedShapes() { return oldModel.shapes().filter(shape -> !newModel.getShape(shape.getId()).isPresent()); } /** * Gets all of the removed shapes of a specific type. * * @param shapeType Type of shape to find. * @param Type of shape. * @return Returns a stream of each removed shape of a specific type. */ public Stream removedShapes(Class shapeType) { return removedShapes().filter(shapeType::isInstance).map(shapeType::cast); } /** * Gets all removed metadata. * *

Each Pair returned contains the name of the metadata key on * the left of the Pair and the metadata value on the right. * * @return Returns a stream of removed metadata. */ public Stream> removedMetadata() { return oldModel.getMetadata().entrySet().stream() .filter(entry -> !newModel.getMetadata().containsKey(entry.getKey())) .map(entry -> Pair.of(entry.getKey(), entry.getValue())); } /** * Gets all changed shapes. * * @return Returns a stream of changed shapes. */ public Stream> changedShapes() { return changedShapes.stream(); } /** * Gets all changed shapes of a specific type. * * @param type Type of shape to find. * @param Type of shape. * @return Returns a stream of matching changed shapes. */ @SuppressWarnings("unchecked") public Stream> changedShapes(Class type) { return changedShapes() .filter(change -> type.isInstance(change.getOldShape()) && type.isInstance(change.getNewShape())) .map(change -> (ChangedShape) change); } /** * Gets a stream of all changed metadata. * * @return Returns the changed metadata. */ public Stream changedMetadata() { return changedMetadata.stream(); } @Override public boolean equals(Object o) { if (this == o) { return true; } else if (!(o instanceof Differences)) { return false; } else { // The differences between the models are always equivalent if the // models are equivalent, so no need to compare them. Differences that = (Differences) o; return getOldModel().equals(that.getOldModel()) && getNewModel().equals(that.getNewModel()); } } @Override public int hashCode() { return Objects.hash(getOldModel(), getNewModel()); } private static void detectShapeChanges(Model oldModel, Model newModel, Differences differences) { for (Shape oldShape : oldModel.toSet()) { newModel.getShape(oldShape.getId()).ifPresent(newShape -> { if (!oldShape.equals(newShape)) { differences.changedShapes.add(new ChangedShape<>(oldShape, newShape)); } }); } } private static void detectMetadataChanges(Model oldModel, Model newModel, Differences differences) { oldModel.getMetadata().forEach((k, v) -> { if (newModel.getMetadata().containsKey(k) && !newModel.getMetadata().get(k).equals(v)) { differences.changedMetadata.add(new ChangedMetadata(k, v, newModel.getMetadata().get(k))); } }); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy