diff --git a/codegen/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/CachingSymbolProvider.java b/codegen/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/CachingSymbolProvider.java new file mode 100644 index 00000000000..fa5f16f8ddf --- /dev/null +++ b/codegen/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/CachingSymbolProvider.java @@ -0,0 +1,45 @@ +/* + * 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.codegen.core; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.shapes.ShapeId; + +/** + * Caches the results of calling {@code toSymbol} and {@code toMemberName}. + */ +final class CachingSymbolProvider implements SymbolProvider { + + private final SymbolProvider delegate; + private final ConcurrentMap<ShapeId, Symbol> symbolCache = new ConcurrentHashMap<>(); + private final ConcurrentMap<ShapeId, String> memberCache = new ConcurrentHashMap<>(); + + CachingSymbolProvider(SymbolProvider delegate) { + this.delegate = delegate; + } + + @Override + public Symbol toSymbol(Shape shape) { + return symbolCache.computeIfAbsent(shape.toShapeId(), id -> delegate.toSymbol(shape)); + } + + @Override + public String toMemberName(Shape shape) { + return memberCache.computeIfAbsent(shape.toShapeId(), id -> delegate.toMemberName(shape)); + } +} diff --git a/codegen/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/SymbolProvider.java b/codegen/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/SymbolProvider.java index ec5c00fd802..cb8285090b8 100644 --- a/codegen/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/SymbolProvider.java +++ b/codegen/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/SymbolProvider.java @@ -73,4 +73,25 @@ public interface SymbolProvider { default String toMemberName(Shape shape) { return shape.getId().getMember().orElseGet(() -> StringUtils.uncapitalize(shape.getId().getName())); } + + /** + * Decorates a {@code SymbolProvider} with a cache and returns the + * decorated {@code SymbolProvider}. + * + * <p>The results of calling {@code toSymbol} and {@code toMemberName} + * on {@code delegate} are cached using a thread-safe cache. + * + * <pre> + * {@code + * SymbolProvider delegate = createComplexProvider(myModel); + * SymbolProvider cachingProvider = SymbolProvider.cache(delegate); + * } + * </pre> + * + * @param delegate Symbol provider to wrap and cache its results. + * @return Returns the wrapped SymbolProvider. + */ + static SymbolProvider cache(SymbolProvider delegate) { + return new CachingSymbolProvider(delegate); + } } diff --git a/codegen/smithy-codegen-core/src/test/java/software/amazon/smithy/codegen/core/CachingSymbolProviderTest.java b/codegen/smithy-codegen-core/src/test/java/software/amazon/smithy/codegen/core/CachingSymbolProviderTest.java new file mode 100644 index 00000000000..f6d18434776 --- /dev/null +++ b/codegen/smithy-codegen-core/src/test/java/software/amazon/smithy/codegen/core/CachingSymbolProviderTest.java @@ -0,0 +1,41 @@ +package software.amazon.smithy.codegen.core; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.equalTo; + +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Test; +import software.amazon.smithy.model.shapes.MemberShape; +import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.shapes.StringShape; + +public class CachingSymbolProviderTest { + @Test + public void cachesResults() { + List<ShapeId> calls = new ArrayList<>(); + + SymbolProvider delegate = shape -> { + calls.add(shape.getId()); + return Symbol.builder().name(shape.getId().getName()).build(); + }; + + SymbolProvider cache = SymbolProvider.cache(delegate); + + Shape a = StringShape.builder().id("foo.baz#A").build(); + Shape b = StringShape.builder().id("foo.baz#B").build(); + Shape c = MemberShape.builder().id("foo.baz#C$c").target(a).build(); + + assertThat(cache.toSymbol(a).getName(), equalTo("A")); + assertThat(cache.toSymbol(b).getName(), equalTo("B")); + assertThat(cache.toSymbol(c).getName(), equalTo("C")); + assertThat(cache.toSymbol(a).getName(), equalTo("A")); + assertThat(cache.toSymbol(b).getName(), equalTo("B")); + assertThat(cache.toSymbol(c).getName(), equalTo("C")); + assertThat(cache.toMemberName(c), equalTo("c")); + + assertThat(calls, containsInAnyOrder(a.getId(), b.getId(), c.getId())); + } +}