1 /*
2  * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License").
5  * You may not use this file except in compliance with the License.
6  * A copy of the License is located at
7  *
8  *  http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */
15 
16 package software.amazon.smithy.go.codegen;
17 
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.List;
21 import java.util.Objects;
22 import java.util.Set;
23 import java.util.TreeSet;
24 import software.amazon.smithy.codegen.core.SymbolDependency;
25 import software.amazon.smithy.codegen.core.SymbolDependencyContainer;
26 import software.amazon.smithy.utils.SetUtils;
27 import software.amazon.smithy.utils.SmithyBuilder;
28 
29 /**
30  *
31  */
32 public final class GoDependency implements SymbolDependencyContainer, Comparable<GoDependency> {
33     private final Type type;
34     private final String sourcePath;
35     private final String importPath;
36     private final String alias;
37     private final String version;
38     private final Set<GoDependency> dependencies;
39     private final SymbolDependency symbolDependency;
40 
GoDependency(Builder builder)41     private GoDependency(Builder builder) {
42         this.type = SmithyBuilder.requiredState("type", builder.type);
43         this.sourcePath = !this.type.equals(Type.STANDARD_LIBRARY)
44                 ? SmithyBuilder.requiredState("sourcePath", builder.sourcePath) : "";
45         this.importPath = SmithyBuilder.requiredState("importPath", builder.importPath);
46         this.alias = builder.alias;
47         this.version = SmithyBuilder.requiredState("version", builder.version);
48         this.dependencies = builder.dependencies;
49 
50         this.symbolDependency = SymbolDependency.builder()
51                 .dependencyType(this.type.toString())
52                 .packageName(this.sourcePath)
53                 .version(this.version)
54                 .build();
55     }
56 
57     /**
58      * Get the the set of {@link GoDependency} required by this dependency.
59      *
60      * @return the set of dependencies
61      */
getGoDependencies()62     public Set<GoDependency> getGoDependencies() {
63         return this.dependencies;
64     }
65 
66     /**
67      * Get the symbol dependency representing the dependency.
68      *
69      * @return the symbol dependency
70      */
getSymbolDependency()71     public SymbolDependency getSymbolDependency() {
72         return symbolDependency;
73     }
74 
75     /**
76      * Get the type of dependency.
77      *
78      * @return the dependency type
79      */
getType()80     public Type getType() {
81         return type;
82     }
83 
84     /**
85      * Get the source code path of the dependency.
86      *
87      * @return the dependency source code path
88      */
getSourcePath()89     public String getSourcePath() {
90         return sourcePath;
91     }
92 
93     /**
94      * Get the import path of the dependency.
95      *
96      * @return the import path of the dependency
97      */
getImportPath()98     public String getImportPath() {
99         return importPath;
100     }
101 
102     /**
103      * Get the alias of the module to use.
104      *
105      * @return the alias
106      */
getAlias()107     public String getAlias() {
108         return alias;
109     }
110 
111     /**
112      * Get the version of the dependency.
113      *
114      * @return the version
115      */
getVersion()116     public String getVersion() {
117         return version;
118     }
119 
120     @Override
getDependencies()121     public List<SymbolDependency> getDependencies() {
122         Set<SymbolDependency> symbolDependencySet = new TreeSet<>(SetUtils.of(getSymbolDependency()));
123 
124         symbolDependencySet.addAll(resolveDependencies(getGoDependencies(), new TreeSet<>(SetUtils.of(this))));
125 
126         return new ArrayList<>(symbolDependencySet);
127     }
128 
resolveDependencies(Set<GoDependency> goDependencies, Set<GoDependency> processed)129     private Set<SymbolDependency> resolveDependencies(Set<GoDependency> goDependencies, Set<GoDependency> processed) {
130         Set<SymbolDependency> symbolDependencies = new TreeSet<>();
131         if (goDependencies.size() == 0) {
132             return symbolDependencies;
133         }
134 
135         Set<GoDependency> dependenciesToResolve = new TreeSet<>();
136         for (GoDependency dependency : goDependencies) {
137             if (processed.contains(dependency)) {
138                 continue;
139             }
140             processed.add(dependency);
141             symbolDependencies.add(dependency.getSymbolDependency());
142             dependenciesToResolve.addAll(dependency.getGoDependencies());
143         }
144 
145         symbolDependencies.addAll(resolveDependencies(dependenciesToResolve, processed));
146 
147         return symbolDependencies;
148     }
149 
builder()150     public static Builder builder() {
151         return new Builder();
152     }
153 
154     @Override
equals(Object o)155     public boolean equals(Object o) {
156         if (this == o) {
157             return true;
158         } else if (!(o instanceof GoDependency)) {
159             return false;
160         }
161 
162         GoDependency other = (GoDependency) o;
163 
164         return this.type.equals(other.type) && this.sourcePath.equals(other.sourcePath)
165                 && this.importPath.equals(other.importPath) && this.alias.equals(other.alias)
166                 && this.version.equals(other.version) && this.dependencies.equals(other.dependencies);
167     }
168 
169     @Override
hashCode()170     public int hashCode() {
171         return Objects.hash(type, sourcePath, importPath, alias, version, dependencies);
172     }
173 
174     @Override
compareTo(GoDependency o)175     public int compareTo(GoDependency o) {
176         if (equals(o)) {
177             return 0;
178         }
179 
180         int importPathCompare = importPath.compareTo(o.importPath);
181         if (importPathCompare != 0) {
182             return importPathCompare;
183         }
184 
185         if (alias != null) {
186             int aliasCompare = alias.compareTo(o.alias);
187             if (aliasCompare != 0) {
188                 return aliasCompare;
189             }
190         }
191 
192         return version.compareTo(o.version);
193     }
194 
195     /**
196      * Represents a dependency type.
197      */
198     public enum Type {
199         STANDARD_LIBRARY, DEPENDENCY;
200 
201         @Override
toString()202         public String toString() {
203             switch (this) {
204                 case STANDARD_LIBRARY:
205                     return "stdlib";
206                 case DEPENDENCY:
207                     return "dependency";
208                 default:
209                     return "unknown";
210             }
211         }
212     }
213 
214     /**
215      * Get {@link GoDependency} representing the provided module description.
216      *
217      * @param sourcePath the root source path for the given code
218      * @param importPath the import path of the package
219      * @param version    the version of source module
220      * @param alias      a default alias to use when importing the package, can be null
221      * @return the dependency
222      */
moduleDependency(String sourcePath, String importPath, String version, String alias)223     public static GoDependency moduleDependency(String sourcePath, String importPath, String version, String alias) {
224         GoDependency.Builder builder = GoDependency.builder()
225                 .type(GoDependency.Type.DEPENDENCY)
226                 .sourcePath(sourcePath)
227                 .importPath(importPath)
228                 .version(version);
229         if (alias != null) {
230             builder.alias(alias);
231         }
232         return builder.build();
233     }
234 
235     /**
236      * Get {@link GoDependency} representing the standard library import description.
237      *
238      * @param importPath the import path of the package
239      * @param version    the version of source module
240      * @return the dependency
241      */
standardLibraryDependency(String importPath, String version)242     public static GoDependency standardLibraryDependency(String importPath, String version) {
243         return GoDependency.builder()
244                 .type(GoDependency.Type.STANDARD_LIBRARY)
245                 .importPath(importPath)
246                 .version(version)
247                 .build();
248     }
249 
250     /**
251      * Get {@link GoDependency} representing the standard library import description.
252      *
253      * @param importPath the import path of the package
254      * @param version    the version of source module
255      * @param alias      the alias for stdlib dependency
256      * @return the dependency
257      */
standardLibraryDependency(String importPath, String version, String alias)258     public static GoDependency standardLibraryDependency(String importPath, String version, String alias) {
259         GoDependency.Builder builder = GoDependency.builder()
260                 .type(GoDependency.Type.STANDARD_LIBRARY)
261                 .importPath(importPath)
262                 .version(version);
263 
264         if (alias != null) {
265             builder.alias(alias);
266         }
267         return builder.build();
268     }
269 
270     /**
271      * Builder for {@link GoDependency}.
272      */
273     public static final class Builder implements SmithyBuilder<GoDependency> {
274         private Type type;
275         private String sourcePath;
276         private String importPath;
277         private String alias;
278         private String version;
279         private final Set<GoDependency> dependencies = new TreeSet<>();
280 
Builder()281         private Builder() {
282         }
283 
284         /**
285          * Set the dependency type.
286          *
287          * @param type dependency type
288          * @return the builder
289          */
type(Type type)290         public Builder type(Type type) {
291             this.type = type;
292             return this;
293         }
294 
295         /**
296          * Set the source path.
297          *
298          * @param sourcePath the source path root
299          * @return the builder
300          */
sourcePath(String sourcePath)301         public Builder sourcePath(String sourcePath) {
302             this.sourcePath = sourcePath;
303             return this;
304         }
305 
306         /**
307          * Set the import path.
308          *
309          * @param importPath the import path
310          * @return the builder
311          */
importPath(String importPath)312         public Builder importPath(String importPath) {
313             this.importPath = importPath;
314             return this;
315         }
316 
317         /**
318          * Set the dependency alias.
319          *
320          * @param alias the alias
321          * @return the builder
322          */
alias(String alias)323         public Builder alias(String alias) {
324             this.alias = alias;
325             return this;
326         }
327 
328         /**
329          * Set the dependency version.
330          *
331          * @param version the version
332          * @return the builder
333          */
version(String version)334         public Builder version(String version) {
335             this.version = version;
336             return this;
337         }
338 
339         /**
340          * Set the collection of {@link GoDependency} required by this dependency.
341          *
342          * @param dependencies a collection of dependencies
343          * @return the builder
344          */
dependencies(Collection<GoDependency> dependencies)345         public Builder dependencies(Collection<GoDependency> dependencies) {
346             this.dependencies.clear();
347             this.dependencies.addAll(dependencies);
348             return this;
349         }
350 
351         /**
352          * Add a dependency on another {@link GoDependency}.
353          *
354          * @param dependency the dependency
355          * @return the builder
356          */
addDependency(GoDependency dependency)357         public Builder addDependency(GoDependency dependency) {
358             this.dependencies.add(dependency);
359             return this;
360         }
361 
362         /**
363          * Remove a dependency on another {@link GoDependency}.
364          *
365          * @param dependency the dependency
366          * @return the builder
367          */
removeDependency(GoDependency dependency)368         public Builder removeDependency(GoDependency dependency) {
369             this.dependencies.remove(dependency);
370             return this;
371         }
372 
373         @Override
build()374         public GoDependency build() {
375             return new GoDependency(this);
376         }
377     }
378 }
379