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

software.amazon.smithy.model.traits.AbstractTrait 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.traits;

import java.util.Objects;
import software.amazon.smithy.model.FromSourceLocation;
import software.amazon.smithy.model.SourceLocation;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.node.ToNode;
import software.amazon.smithy.model.shapes.ShapeId;

/**
 * Base implementation of traits.
 *
 * 

This implementation provides an {@link #equals} and {@link #hashCode} * that should work for most traits that extend from this base class. Note * that equality for traits that extend from this type are not based on the * concrete class, but rather the trait name and the trait's {@link ToNode} * representation. * *

The Node value of a trait can be provided when the trait is created * using {@link #setNodeCache(Node)}. Note that when setting the node cache, * the equality and hashcode of the trait are impacted because they are by * default based on the{@link #toNode()} value of a trait. This typically * isn't an issue until model transformations are performed that modify a * trait. In these cases, the original node value of the trait might differ * from the updated trait even if they are semantically the same value (for * example, if the only change to the trait is modifying its source location, * or if a property of the trait was explicitly set to false, but false is * omitted when serializing the updated trait to a node value). If this use * case needs to be accounted for, you must override equals and hashCode of * the trait. */ public abstract class AbstractTrait implements Trait { private final transient ShapeId traitId; private final transient SourceLocation traitSourceLocation; private transient int cachedHashCode = 0; private transient Node nodeCache; /** * @param id ID of the trait. * @param sourceLocation Where the trait was defined. */ public AbstractTrait(ShapeId id, FromSourceLocation sourceLocation) { this.traitId = Objects.requireNonNull(id, "id was not set on trait"); this.traitSourceLocation = Objects.requireNonNull(sourceLocation, "sourceLocation was not set on trait") .getSourceLocation(); } /** * @param id ID of the trait. * @param nodeValue The node representation of the shape, if known and trusted. */ public AbstractTrait(ShapeId id, Node nodeValue) { this(id, nodeValue.getSourceLocation()); setNodeCache(nodeValue); } @Override public final ShapeId toShapeId() { return traitId; } @Override public final SourceLocation getSourceLocation() { return traitSourceLocation; } @Override public String toString() { return String.format("Trait `%s`, defined at %s", toShapeId(), getSourceLocation()); } @Override public boolean equals(Object other) { if (other == this) { return true; } else if (!(other instanceof Trait)) { return false; } else if (hashCode() != other.hashCode()) { // take advantage of hashcode caching return false; } else { Trait b = (Trait) other; return toShapeId().equals(b.toShapeId()) && toNode().equals(b.toNode()); } } @Override public int hashCode() { if (cachedHashCode == 0) { cachedHashCode = toShapeId().hashCode() * 17 + toNode().hashCode(); } return cachedHashCode; } @Override public final Node toNode() { if (nodeCache == null) { setNodeCache(createNode()); } return nodeCache; } /** * The result of toNode is used for hashCodes and equality. Subclasses * must implement createNode to turn the trait into a Node. This is then * cached for subsequent retrievals. * * @return Returns the trait as a node. */ protected abstract Node createNode(); /** * Sets the node cache of the trait up front, if known. * *

This is useful for maintaining a trait value exactly as provided in * a model file, allowing for validation to detect extraneous properties, * and removing the need to create the node again when calling createNode. * * @param value Value to set. */ protected final void setNodeCache(Node value) { this.nodeCache = value; } /** * Basic provider implementation that returns the name of the * provided trait. */ public abstract static class Provider implements TraitService { private final ShapeId id; /** * @param id ID of the trait that the provider creates. */ public Provider(ShapeId id) { this.id = id; } @Override public ShapeId getShapeId() { return id; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy