|
22 | 22 | import java.util.function.Predicate; |
23 | 23 | import java.util.function.Supplier; |
24 | 24 | import java.util.logging.Logger; |
| 25 | +import software.amazon.smithy.build.model.ProjectionConfig; |
25 | 26 | import software.amazon.smithy.build.model.SmithyBuildConfig; |
26 | 27 | import software.amazon.smithy.model.Model; |
27 | 28 | import software.amazon.smithy.model.loader.ModelAssembler; |
28 | 29 | import software.amazon.smithy.model.transform.ModelTransformer; |
| 30 | +import software.amazon.smithy.model.validation.ValidatedResult; |
| 31 | +import software.amazon.smithy.utils.FunctionalUtils; |
29 | 32 |
|
30 | 33 | /** |
31 | 34 | * Runs the projections and plugins found in a {@link SmithyBuildConfig} |
@@ -167,6 +170,102 @@ public void build(Consumer<ProjectionResult> resultCallback, BiConsumer<String, |
167 | 170 | new SmithyBuildImpl(this).applyAllProjections(resultCallback, exceptionCallback); |
168 | 171 | } |
169 | 172 |
|
| 173 | + /** |
| 174 | + * Assembles the model from the given configuration and applies the transforms |
| 175 | + * of the named projection, returning the projected model and its validation |
| 176 | + * events without running any plugins or writing artifacts to disk. Both |
| 177 | + * {@link SmithyBuildConfig#getSources()} and {@link SmithyBuildConfig#getImports()} |
| 178 | + * are loaded into the model. |
| 179 | + * |
| 180 | + * <p>If the base-model assemble is broken, the base result is returned |
| 181 | + * unchanged so the caller can inspect its events. |
| 182 | + * |
| 183 | + * <p>The class loader used for service discovery is the |
| 184 | + * {@linkplain Thread#getContextClassLoader() current thread's context |
| 185 | + * class loader}, falling back to {@link SmithyBuild}'s class loader. Use |
| 186 | + * {@link #toProjectedModel(SmithyBuildConfig, String, ClassLoader)} when |
| 187 | + * the loader needs to be controlled explicitly (for example, in IDE or |
| 188 | + * application-server environments). |
| 189 | + * |
| 190 | + * @param config Configuration to load. |
| 191 | + * @param projectionName The projection to apply. |
| 192 | + * @return The validated projected model. |
| 193 | + * @throws UnknownProjectionException if the projection is not declared. |
| 194 | + * @throws SmithyBuildException if the projection is abstract or the build |
| 195 | + * fails. |
| 196 | + */ |
| 197 | + public static ValidatedResult<Model> toProjectedModel(SmithyBuildConfig config, String projectionName) { |
| 198 | + ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); |
| 199 | + return toProjectedModel(config, |
| 200 | + projectionName, |
| 201 | + contextLoader != null ? contextLoader : SmithyBuild.class.getClassLoader()); |
| 202 | + } |
| 203 | + |
| 204 | + /** |
| 205 | + * Assembles the model from the given configuration and applies the transforms |
| 206 | + * of the named projection, using the given class loader for service |
| 207 | + * discovery of traits, validators, etc., returning the projected model and |
| 208 | + * its validation events without running any plugins. Both |
| 209 | + * {@link SmithyBuildConfig#getSources()} and {@link SmithyBuildConfig#getImports()} |
| 210 | + * are loaded into the model. |
| 211 | + * |
| 212 | + * <p>If the base-model assemble is broken, the base result is returned |
| 213 | + * unchanged so the caller can inspect its events. |
| 214 | + * |
| 215 | + * @param config Configuration to load. |
| 216 | + * @param projectionName The projection to apply. |
| 217 | + * @param classLoader Class loader used for service discovery. |
| 218 | + * @return The validated projected model. |
| 219 | + * @throws UnknownProjectionException if the projection is not declared. |
| 220 | + * @throws SmithyBuildException if the projection is abstract or the build |
| 221 | + * fails. |
| 222 | + */ |
| 223 | + public static ValidatedResult<Model> toProjectedModel( |
| 224 | + SmithyBuildConfig config, |
| 225 | + String projectionName, |
| 226 | + ClassLoader classLoader |
| 227 | + ) { |
| 228 | + ProjectionConfig projection = config.getProjections().get(projectionName); |
| 229 | + if (projection == null) { |
| 230 | + throw new UnknownProjectionException("Unknown projection: " + projectionName); |
| 231 | + } |
| 232 | + if (projection.isAbstract()) { |
| 233 | + throw new SmithyBuildException("Cannot apply abstract projection: " + projectionName); |
| 234 | + } |
| 235 | + |
| 236 | + ValidatedResult<Model> baseResult = config.toModelAssembler(classLoader).assemble(); |
| 237 | + if (baseResult.isBroken()) { |
| 238 | + // The inner pipeline unwraps the base model and would throw on ERROR/DANGER events, |
| 239 | + // so short-circuit and hand the broken result to the caller intact. |
| 240 | + return baseResult; |
| 241 | + } |
| 242 | + |
| 243 | + // Restrict the run config to the targeted projection so SmithyBuildImpl does not eagerly |
| 244 | + // resolve transformers for unrelated projections (which would throw on configs that mention |
| 245 | + // transforms not on this classloader). |
| 246 | + SmithyBuildConfig runConfig = config.toBuilder() |
| 247 | + .projections(Collections.singletonMap(projectionName, projection)) |
| 248 | + // Strip sources and imports because the pre-assembled base model is passed via |
| 249 | + // .model() below, leaving them would re-parse the same files |
| 250 | + .sources(Collections.emptyList()) |
| 251 | + .imports(Collections.emptyList()) |
| 252 | + // Set ignoreMissingPlugins because plugin resolution still walks declared plugins |
| 253 | + // even though the pluginFilter rejects them all. |
| 254 | + .ignoreMissingPlugins(true) |
| 255 | + .build(); |
| 256 | + |
| 257 | + ProjectionResult result = SmithyBuild.create(classLoader) |
| 258 | + .config(runConfig) |
| 259 | + .model(baseResult.getResult().get()) |
| 260 | + .pluginFilter(FunctionalUtils.alwaysFalse()) |
| 261 | + .build() |
| 262 | + .getProjectionResult(projectionName) |
| 263 | + .orElseThrow(() -> new IllegalStateException( |
| 264 | + "Projection result not found: " + projectionName)); |
| 265 | + |
| 266 | + return new ValidatedResult<>(result.getModel(), result.getEvents()); |
| 267 | + } |
| 268 | + |
170 | 269 | /** |
171 | 270 | * Sets the <strong>required</strong> configuration object used to |
172 | 271 | * build the model. |
|
0 commit comments