Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to alias referenced symbols #168

Merged
merged 1 commit into from
Sep 19, 2019
Merged
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 @@ -16,9 +16,7 @@
package software.amazon.smithy.codegen.core;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import software.amazon.smithy.utils.ListUtils;
import software.amazon.smithy.utils.SmithyBuilder;
Expand Down Expand Up @@ -229,13 +227,15 @@ public int hashCode() {
/**
* Builds a Symbol.
*/
public static final class Builder implements SmithyBuilder<Symbol> {
public static final class Builder
extends TypedPropertiesBag.Builder<Builder>
implements SmithyBuilder<Symbol> {

private String name;
private String namespace = "";
private String namespaceDelimiter = "";
private String definitionFile = "";
private String declarationFile = "";
private Map<String, Object> properties = new HashMap<>();
private List<SymbolReference> references = new ArrayList<>();

@Override
Expand Down Expand Up @@ -267,41 +267,6 @@ public Builder namespace(String namespace, String namespaceDelimiter) {
return this;
}

/**
* Sets a specific custom property.
*
* @param key Key to set.
* @param value Value to set.
* @return Returns the builder.
*/
public Builder putProperty(String key, Object value) {
properties.put(key, value);
return this;
}

/**
* Removes a specific custom property.
*
* @param key Key to remove.
* @return Returns the builder.
*/
public Builder removeProperty(String key) {
properties.remove(key);
return this;
}

/**
* Replaces all of the custom properties.
*
* @param properties Custom properties to replace with.
* @return Returns the builder.
*/
public Builder properties(Map<String, Object> properties) {
this.properties.clear();
this.properties.putAll(properties);
return this;
}

/**
* Sets the filename of where this symbol is defined.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import software.amazon.smithy.utils.SmithyBuilder;
import software.amazon.smithy.utils.ToSmithyBuilder;

/**
* Represents a reference from a Symbol to another Symbol.
Expand All @@ -40,7 +42,7 @@
* {@link ContextOption#USE} options, meaning that the reference is
* necessary both when defining and when using a symbol.
*/
public final class SymbolReference extends TypedPropertiesBag {
public final class SymbolReference extends TypedPropertiesBag implements ToSmithyBuilder<SymbolReference> {

/**
* Top-level interface for all {@code SymbolReference} options.
Expand All @@ -65,6 +67,7 @@ public enum ContextOption implements Option {

private final Symbol symbol;
private final Set<Option> options;
private final String alias;

/**
* @param symbol Symbol that is referenced.
Expand All @@ -80,20 +83,32 @@ public SymbolReference(Symbol symbol, Option... options) {
* @param options Options to store with the reference.
*/
public SymbolReference(Symbol symbol, Map<String, Object> properties, Option... options) {
super(properties);
this.symbol = symbol;
this(new Builder().symbol(symbol).properties(properties).options(options));
}

private SymbolReference(Builder builder) {
super(builder.properties);
this.symbol = SmithyBuilder.requiredState("symbol", builder.symbol);
this.alias = builder.alias == null ? builder.symbol.getName() : builder.alias;

Set<Option> opts = new HashSet<>(options.length + 2);
if (options.length == 0) {
Set<Option> opts = new HashSet<>(builder.options.size() + 2);
if (builder.options.size() == 0) {
opts.add(ContextOption.USE);
opts.add(ContextOption.DECLARE);
} else {
Collections.addAll(opts, options);
opts.addAll(builder.options);
}

this.options = Collections.unmodifiableSet(opts);
}

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

/**
* Gets the referenced symbol.
*
Expand All @@ -103,6 +118,22 @@ public Symbol getSymbol() {
return symbol;
}

/**
* Gets the alias to use when referring to the Symbol.
*
* <p>The value of {@code getSymbol().getName()} is returned if no
* alias was explicitly configured on the reference.
*
* <p>An alias is used in some programming languages to change the
* way a symbol is referenced in a source file. Aliases are often used
* for de-conflicting symbols.
*
* @return Returns the alias.
*/
public String getAlias() {
return alias;
}

/**
* Gets all of the reference options.
*
Expand All @@ -122,6 +153,15 @@ public boolean hasOption(Option option) {
return options.contains(option);
}

@Override
public Builder toBuilder() {
return new Builder()
.symbol(symbol)
.options(options)
.properties(getProperties())
.alias(alias);
}

@Override
public boolean equals(Object o) {
if (this == o) {
Expand All @@ -133,16 +173,85 @@ public boolean equals(Object o) {
SymbolReference that = (SymbolReference) o;
return symbol.equals(that.symbol)
&& getProperties().equals(that.getProperties())
&& options.equals(that.options);
&& options.equals(that.options)
&& alias.equals(that.alias);
}

@Override
public int hashCode() {
return Objects.hash(symbol, getProperties());
return Objects.hash(symbol, alias, getProperties());
}

@Override
public String toString() {
return "SymbolReference{symbol=" + symbol + ", options=" + options + '}';
return "SymbolReference{symbol=" + symbol + ", alias='" + alias + "', options=" + options + "}";
}

/**
* Builds a SymbolReference.
*/
public static final class Builder
extends TypedPropertiesBag.Builder<Builder>
implements SmithyBuilder<SymbolReference> {

private Symbol symbol;
private Set<Option> options = new HashSet<>();
private String alias;

private Builder() {}

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

/**
* Sets the Symbol referenced by the SymbolReference.
*
* @param symbol Symbol to reference.
* @return Returns the builder.
*/
public Builder symbol(Symbol symbol) {
this.symbol = symbol;
return this;
}

/**
* Adds a Set of Options to the SymbolReference.
*
* @param options Options to add.
* @return Returns the builder.
*/
public Builder options(Set<Option> options) {
this.options = options;
return this;
}

/**
* Adds an array of Options to the SymbolReference.
*
* @param options Options to add.
* @return Returns the builder.
*/
public Builder options(Option... options) {
this.options = new HashSet<>();
Collections.addAll(this.options, options);
return this;
}

/**
* Adds an alias to the SymbolReference.
*
* <p>An alias is used in some programming languages to change the
* way a symbol is referenced in a source file. Aliases are often used
* for de-conflicting symbols.
*
* @param alias Alias to assign the symbol.
* @return Returns the builder.
*/
public Builder alias(String alias) {
this.alias = alias;
return this;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

package software.amazon.smithy.codegen.core;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import software.amazon.smithy.utils.MapUtils;
Expand Down Expand Up @@ -95,4 +96,49 @@ public <T> T expectProperty(String name, Class<T> type) {
return getProperty(name, type).orElseThrow(() -> new IllegalArgumentException(String.format(
"Property `%s` is not part of %s, `%s`", name, getClass().getSimpleName(), this)));
}

/**
* Builds a SymbolReference.
*/
abstract static class Builder<T extends Builder> {
Map<String, Object> properties = new HashMap<>();

/**
* Sets a specific custom property.
*
* @param key Key to set.
* @param value Value to set.
* @return Returns the builder.
*/
@SuppressWarnings("unchecked")
public T putProperty(String key, Object value) {
properties.put(key, value);
return (T) this;
}

/**
* Removes a specific custom property.
*
* @param key Key to remove.
* @return Returns the builder.
*/
@SuppressWarnings("unchecked")
public T removeProperty(String key) {
properties.remove(key);
return (T) this;
}

/**
* Replaces all of the custom properties.
*
* @param properties Custom properties to replace with.
* @return Returns the builder.
*/
@SuppressWarnings("unchecked")
public T properties(Map<String, Object> properties) {
this.properties.clear();
this.properties.putAll(properties);
return (T) this;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package software.amazon.smithy.codegen.core;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
Expand Down Expand Up @@ -31,4 +32,27 @@ public void propertiesAndOptionsAreUsedInEquals() {
assertThat(ref1, not(equalTo(ref3)));
assertThat(ref2, not(equalTo(ref3)));
}

@Test
public void canAssignAlias() {
Symbol symbol = Symbol.builder().name("foo").build();
SymbolReference ref1 = SymbolReference.builder().symbol(symbol).alias("$foo").build();
SymbolReference ref2 = new SymbolReference(symbol);

assertThat(ref1, not(equalTo(ref2)));
assertThat(ref1.getSymbol().getName(), equalTo("foo"));
assertThat(ref1.getAlias(), equalTo("$foo"));
assertThat(ref1.toString(), containsString("'$foo'"));

assertThat(ref2.getSymbol().getName(), equalTo("foo"));
assertThat(ref2.getAlias(), equalTo("foo"));
}

@Test
public void convertsToBuilder() {
Symbol symbol = Symbol.builder().name("foo").build();
SymbolReference ref1 = SymbolReference.builder().symbol(symbol).alias("$foo").build();

assertThat(ref1.toBuilder().build(), equalTo(ref1));
}
}