Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,8 @@ public static String toUpperSnakeCase(String string) {
*/
public static Symbol getInnerTypeEnumSymbol(Symbol symbol) {
return symbol.toBuilder()
.namespace(symbol.getFullName(), ".")
.putProperty(SymbolProperties.IS_LOCALLY_DEFINED, true)
.name("Type")
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ public final class SymbolProperties {
*/
public static final Property<Boolean> IS_JAVA_ARRAY = Property.named("is-java-array");

/**
* Indicates if a symbol represents an inner class.
*/
public static final Property<Boolean> IS_LOCALLY_DEFINED = Property.named("is-locally-defined");

/**
* Method on {@link java.util.Collection} to use to create an immutable copy of the collection type
* a symbol represents.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ public void accept(GenerateUnionDirective<CodeGenerationContext, JavaCodegenSett
}
var shape = directive.shape();
directive.context().writerDelegator().useShapeWriter(shape, writer -> {
var innerTypeEnumSymbol = CodegenUtils.getInnerTypeEnumSymbol(directive.symbol());
writer.addLocallyDefinedSymbol(innerTypeEnumSymbol);
writer.pushState(new ClassSection(shape));
var template = """
public abstract class ${shape:T} implements ${serializableStruct:T} {
Expand Down Expand Up @@ -97,7 +99,7 @@ public boolean equals(${object:T} other) {
}
""";
writer.putContext("shape", directive.symbol());
writer.putContext("type", CodegenUtils.getInnerTypeEnumSymbol(directive.symbol()));
writer.putContext("type", innerTypeEnumSymbol);
writer.putContext("serializableStruct", SerializableStruct.class);
writer.putContext("shapeSerializer", ShapeSerializer.class);
writer.putContext("schemaClass", Schema.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ final class JavaImportContainer implements ImportContainer {

@Override
public void importSymbol(Symbol symbol, String s) {
// Do not import primitive types or java.lang standard library imports.
if (symbol.expectProperty(SymbolProperties.IS_PRIMITIVE) || symbol.getNamespace().startsWith("java.lang")) {
// Do not import primitive types, java.lang standard library imports or inner classes.
if (symbol.expectProperty(SymbolProperties.IS_PRIMITIVE)
|| symbol.getNamespace().startsWith("java.lang")
|| symbol.getProperty(SymbolProperties.IS_LOCALLY_DEFINED).isPresent()) {
return;
}
Set<Symbol> duplicates = imports.computeIfAbsent(symbol.getName(), sn -> new HashSet<>());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

package software.amazon.smithy.java.codegen.writer;

import java.util.HashSet;
import java.util.Set;
import java.util.function.BiFunction;
import software.amazon.smithy.codegen.core.Symbol;
Expand Down Expand Up @@ -33,6 +34,7 @@ public final class JavaWriter extends DeferredSymbolWriter<JavaWriter, JavaImpor
private final String packageNamespace;
private final JavaCodegenSettings settings;
private final String filename;
private final Set<String> locallyDefinedNames = new HashSet<>();

public JavaWriter(JavaCodegenSettings settings, String packageNamespace, String filename) {
super(new JavaImportContainer(packageNamespace));
Expand Down Expand Up @@ -87,28 +89,56 @@ public void newLine() {
private void putNameContext() {
// Add any implicit usages from classes in the same package
var packageSymbols = settings.getGeneratedSymbolsPackage(packageNamespace);
packageSymbols.forEach(this::addToSymbolTable);
for (final Set<Symbol> duplicates : symbolTable.values()) {
Set<String> packageDefinedNames = new HashSet<>();
for (Symbol packageSymbol : packageSymbols) {
packageDefinedNames.add(packageSymbol.getName());
}
for (Set<Symbol> duplicates : symbolTable.values()) {
// If the duplicates list has more than one entry
// then duplicates are present, and we need to de-duplicate the names
if (duplicates.size() > 1) {
duplicates.forEach(dupe -> putContext(dupe.getFullName(), deduplicate(dupe)));
} else {
Symbol symbol = duplicates.iterator().next();
putContext(symbol.getFullName().replace("[]", "Array"), symbol.getName());
// Use fully qualified names for java.lang.* if there is duplicate names
// defined under the same package.
String symbolName = symbol.getName();
if (packageDefinedNames.contains(symbol.getName()) && symbol.getNamespace().equals("java.lang")) {
symbolName = symbol.getFullName();
}
putContext(symbol.getFullName(), symbolName);
}
}
}

private String deduplicate(Symbol dupe) {
// If we are in the namespace of a Symbol, use its
// short name, otherwise use the full name
if (dupe.getNamespace().equals(packageNamespace)) {
if (useSimpleName(dupe)) {
return dupe.getName();
}
return dupe.getFullName();
}

private boolean useSimpleName(Symbol dupe) {
// Only locally defined symbols and those symbols with non-conflicting names
// under the same namespace can use short name.
if (locallyDefinedNames.contains(dupe.getName())) {
return dupe.getProperty(SymbolProperties.IS_LOCALLY_DEFINED).isPresent();
}
return dupe.getNamespace().equals(packageNamespace);
}

/**
* Records the local name defined within the current file, e.g., the name of an inner class.
* The writer will use this information to decide if it should use a fully qualified
* name when referencing types with the same name in its scope.
*
* @param symbol the symbol reserved in this file.
*/
public void addLocallyDefinedSymbol(Symbol symbol) {
locallyDefinedNames.add(symbol.getName());
symbolTable.computeIfAbsent(symbol.getName(), k -> new HashSet<>()).add(symbol);
}

/**
* A factory class to create {@link JavaWriter}s.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ double
else
enum
extends
false
final
finally
float
Expand All @@ -33,13 +34,13 @@ goto
if
implements
import
type
instanceof
int
interface
long
native
new
null
object
package
private
Expand All @@ -57,9 +58,7 @@ throw
throws
transient
try
true
void
volatile
while
true
false
null
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@

package software.amazon.smithy.java.example.standalone.model;

import java.util.Objects;
import software.amazon.smithy.java.core.schema.Schema;
import software.amazon.smithy.java.core.schema.SerializableStruct;
import software.amazon.smithy.java.core.schema.ShapeBuilder;
import software.amazon.smithy.java.core.serde.ShapeDeserializer;
import software.amazon.smithy.java.core.serde.ShapeSerializer;
import software.amazon.smithy.java.core.serde.ToStringSerializer;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.utils.SmithyGenerated;

@SmithyGenerated
public final class BuilderShape implements SerializableStruct {

public static final Schema $SCHEMA = Schemas.BUILDER;

public static final ShapeId $ID = $SCHEMA.id();

private BuilderShape(Builder builder) {
}

@Override
public String toString() {
return ToStringSerializer.serialize(this);
}

@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}
return other != null && getClass() == other.getClass();
}

@Override
public int hashCode() {
return Objects.hash();
}

@Override
public Schema schema() {
return $SCHEMA;
}

@Override
public void serializeMembers(ShapeSerializer serializer) {

}

@Override
@SuppressWarnings("unchecked")
public <T> T getMemberValue(Schema member) {
throw new IllegalArgumentException("Attempted to get non-existent member: " + member.id());
}

/**
* Create a new builder containing all the current property values of this object.
*
* <p><strong>Note:</strong> This method performs only a shallow copy of the original properties.
*
* @return a builder for {@link BuilderShape}.
*/
public Builder toBuilder() {
var builder = new Builder();
return builder;
}

/**
* @return returns a new Builder.
*/
public static Builder builder() {
return new Builder();
}

/**
* Builder for {@link BuilderShape}.
*/
public static final class Builder implements ShapeBuilder<BuilderShape> {

private Builder() {}

@Override
public Schema schema() {
return $SCHEMA;
}

@Override
public BuilderShape build() {
return new BuilderShape(this);
}

@Override
public Builder deserialize(ShapeDeserializer decoder) {
decoder.readStruct($SCHEMA, this, $InnerDeserializer.INSTANCE);
return this;
}

@Override
public Builder deserializeMember(ShapeDeserializer decoder, Schema schema) {
decoder.readStruct(schema.assertMemberTargetIs($SCHEMA), this, $InnerDeserializer.INSTANCE);
return this;
}

private static final class $InnerDeserializer implements ShapeDeserializer.StructMemberConsumer<Builder> {
private static final $InnerDeserializer INSTANCE = new $InnerDeserializer();

@Override
public void accept(Builder builder, Schema member, ShapeDeserializer de) {}
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@

package software.amazon.smithy.java.example.standalone.model;

import java.util.Objects;
import software.amazon.smithy.java.core.schema.Schema;
import software.amazon.smithy.java.core.schema.SerializableStruct;
import software.amazon.smithy.java.core.schema.ShapeBuilder;
import software.amazon.smithy.java.core.serde.ShapeDeserializer;
import software.amazon.smithy.java.core.serde.ShapeSerializer;
import software.amazon.smithy.java.core.serde.ToStringSerializer;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.utils.SmithyGenerated;

@SmithyGenerated
public final class List implements SerializableStruct {

public static final Schema $SCHEMA = Schemas.LIST;

public static final ShapeId $ID = $SCHEMA.id();

private List(Builder builder) {
}

@Override
public String toString() {
return ToStringSerializer.serialize(this);
}

@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}
return other != null && getClass() == other.getClass();
}

@Override
public int hashCode() {
return Objects.hash();
}

@Override
public Schema schema() {
return $SCHEMA;
}

@Override
public void serializeMembers(ShapeSerializer serializer) {

}

@Override
@SuppressWarnings("unchecked")
public <T> T getMemberValue(Schema member) {
throw new IllegalArgumentException("Attempted to get non-existent member: " + member.id());
}

/**
* Create a new builder containing all the current property values of this object.
*
* <p><strong>Note:</strong> This method performs only a shallow copy of the original properties.
*
* @return a builder for {@link List}.
*/
public Builder toBuilder() {
var builder = new Builder();
return builder;
}

/**
* @return returns a new Builder.
*/
public static Builder builder() {
return new Builder();
}

/**
* Builder for {@link List}.
*/
public static final class Builder implements ShapeBuilder<List> {

private Builder() {}

@Override
public Schema schema() {
return $SCHEMA;
}

@Override
public List build() {
return new List(this);
}

@Override
public Builder deserialize(ShapeDeserializer decoder) {
decoder.readStruct($SCHEMA, this, $InnerDeserializer.INSTANCE);
return this;
}

@Override
public Builder deserializeMember(ShapeDeserializer decoder, Schema schema) {
decoder.readStruct(schema.assertMemberTargetIs($SCHEMA), this, $InnerDeserializer.INSTANCE);
return this;
}

private static final class $InnerDeserializer implements ShapeDeserializer.StructMemberConsumer<Builder> {
private static final $InnerDeserializer INSTANCE = new $InnerDeserializer();

@Override
public void accept(Builder builder, Schema member, ShapeDeserializer de) {}
}
}
}

Loading