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