Skip to content

Commit

Permalink
add validation step before code generation
Browse files Browse the repository at this point in the history
  • Loading branch information
Sebastian Hoß committed May 31, 2022
1 parent 194ec8c commit 01f8a20
Show file tree
Hide file tree
Showing 37 changed files with 347 additions and 169 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ public enum ApplicationErrors {
*/
CODE_GENERATION_FAILED,

/**
* Signals that runtime environment is invalid.
*/
RUNTIME_INVALID,

/**
* Signals that names configuration is invalid.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ public enum CodegenLifecycle {
GENERATE_REPOSITORIES,

/**
* Signals that utilities are about to be generated.
* Signals that converters are about to be generated.
*/
GENERATE_UTILITIES,
GENERATE_CONVERTERS,

}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@ public void codeGenerationException(final String message) {
throwWith(new CodeGenerationException(message));
}

/**
* @param message The message to include.
*/
public void runtimeException(final String message) {
throwWith(new RuntimeException(message));
}

/**
* @param message The message to include.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* This file is part of yosql. It is subject to the license terms in the LICENSE file found in the top-level
* directory of this distribution and at https://creativecommons.org/publicdomain/zero/1.0/. No part of yosql,
* including this file, may be copied, modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/

package wtf.metio.yosql.codegen.lifecycle;

import ch.qos.cal10n.BaseName;
import ch.qos.cal10n.Locale;
import ch.qos.cal10n.LocaleData;

@LocaleData({@Locale("en"), @Locale("de")})
@BaseName("validation-lifecycle")
public enum ValidationLifecycle {

/**
* Signals that the configuration is about to be validated.
*/
VALIDATE_CONFIGURATION,

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,66 @@
* including this file, may be copied, modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/

package wtf.metio.yosql.codegen.orchestration;

import ch.qos.cal10n.IMessageConveyor;
import wtf.metio.yosql.codegen.dao.CodeGenerator;
import wtf.metio.yosql.codegen.files.FileParser;
import wtf.metio.yosql.codegen.lifecycle.*;
import wtf.metio.yosql.codegen.validation.RuntimeValidator;
import wtf.metio.yosql.models.immutables.PackagedTypeSpec;
import wtf.metio.yosql.models.immutables.SqlStatement;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;

import static java.util.concurrent.CompletableFuture.supplyAsync;

public final class DefaultOrchestrator implements Orchestrator {
/**
* Default implementation of YoSQL. It's responsible for the high-level orchestration of a single code generation run
* (which can be called multiple times if desired). Delegates most of the actual work to various interfaces whose
* implementation must be provided at runtime.
*/
public final class DefaultYoSQL implements YoSQL {

private final FileParser fileParser;
private final CodeGenerator codeGenerator;
private final Executor pool;
private final Timer timer;
private final IMessageConveyor messages;
private final TypeWriter typeWriter;
private final ExecutionErrors errors;
private final RuntimeValidator validator;

public DefaultOrchestrator(
public DefaultYoSQL(
final FileParser fileParser,
final CodeGenerator codeGenerator,
final Executor pool,
final Timer timer,
final IMessageConveyor messages,
final TypeWriter typeWriter,
final ExecutionErrors errors) {
final ExecutionErrors errors,
final RuntimeValidator validator) {
this.fileParser = fileParser;
this.codeGenerator = codeGenerator;
this.pool = pool;
this.timer = timer;
this.errors = errors;
this.messages = messages;
this.typeWriter = typeWriter;
this.errors = errors;
this.validator = validator;
}

@Override
public void execute(
final Supplier<List<SqlStatement>> parser,
final Function<List<SqlStatement>, Stream<PackagedTypeSpec>> generateCode) {
supplyAsync(() -> parseFiles(parser), pool)
.thenApplyAsync((statements -> timeCodeGeneration(generateCode, statements)), pool)
public void generateCode() {
timer.timed(messages.getMessage(ValidationLifecycle.VALIDATE_CONFIGURATION), validator::validate);
if (errors.hasErrors()) {
errors.runtimeException(messages.getMessage(ApplicationErrors.RUNTIME_INVALID));
}
supplyAsync(this::parseFiles, pool)
.thenApplyAsync(this::timeCodeGeneration, pool)
.thenAcceptAsync(this::writeIntoFiles, pool)
.thenRunAsync(timer::printTimings, pool)
.exceptionally(this::handleExceptions)
Expand All @@ -56,30 +73,28 @@ public void execute(
}
}

private List<SqlStatement> parseFiles(final Supplier<List<SqlStatement>> parser) {
final var statements = timer.timed(messages.getMessage(ParseLifecycle.PARSE_FILES), parser);
private List<SqlStatement> parseFiles() {
final var statements = timer.timed(messages.getMessage(ParseLifecycle.PARSE_FILES),
fileParser::parseFiles);
if (errors.hasErrors()) {
errors.sqlFileParsingException(messages.getMessage(ApplicationErrors.PARSE_FILES_FAILED));
}
return statements;
}

private Stream<PackagedTypeSpec> timeCodeGeneration(
final Function<List<SqlStatement>, Stream<PackagedTypeSpec>> generateCode,
final List<SqlStatement> statements) {
return timer.timed(messages.getMessage(CodegenLifecycle.GENERATE_REPOSITORIES), () -> generateCode.apply(statements));
return timer.timed(messages.getMessage(CodegenLifecycle.GENERATE_REPOSITORIES),
() -> codeGenerator.generateCode(statements));
}

private void writeIntoFiles(final Stream<PackagedTypeSpec> typeSpecs) {
timer.timed(messages.getMessage(WriteLifecycle.WRITE_FILES), writeTypeSpecs(typeSpecs));
}

private Runnable writeTypeSpecs(final Stream<PackagedTypeSpec> typeSpecs) {
return () -> typeSpecs.parallel().forEach(typeWriter::writeType);
timer.timed(messages.getMessage(WriteLifecycle.WRITE_FILES),
() -> typeSpecs.parallel().forEach(typeWriter::writeType));
}

private Void handleExceptions(final Throwable throwable) {
errors.add(throwable.getCause());
errors.add(Objects.requireNonNullElse(throwable.getCause(), throwable));
return null;
}

Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,11 @@
* High-level interface of YoSQL. All configuration options must be passed into the actual implementation
* or otherwise obtained before generating code.
*/
@FunctionalInterface
public interface YoSQL {

/**
* Generates .java files based on the configured .sql files and generator options.
*/
void generateCode();

// TODO: add method that validates the supplied RuntimeConfiguration

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* This file is part of yosql. It is subject to the license terms in the LICENSE file found in the top-level
* directory of this distribution and at https://creativecommons.org/publicdomain/zero/1.0/. No part of yosql,
* including this file, may be copied, modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/

package wtf.metio.yosql.codegen.validation;

import wtf.metio.yosql.models.immutables.RuntimeConfiguration;

public final class DefaultRuntimeValidator implements RuntimeValidator {

private final RuntimeConfiguration runtimeConfiguration;
private final RuntimeConfigurationValidator runtimeConfigurationValidator;

public DefaultRuntimeValidator(
final RuntimeConfiguration runtimeConfiguration,
final RuntimeConfigurationValidator runtimeConfigurationValidator) {
this.runtimeConfiguration = runtimeConfiguration;
this.runtimeConfigurationValidator = runtimeConfigurationValidator;
}

@Override
public void validate() {
runtimeConfigurationValidator.validate(runtimeConfiguration);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* This file is part of yosql. It is subject to the license terms in the LICENSE file found in the top-level
* directory of this distribution and at https://creativecommons.org/publicdomain/zero/1.0/. No part of yosql,
* including this file, may be copied, modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/

package wtf.metio.yosql.codegen.validation;

import wtf.metio.yosql.models.immutables.RuntimeConfiguration;

import java.util.Set;

/**
* Delegates {@link RuntimeConfiguration} validation to a set of {@link RuntimeConfigurationValidator}s.
*/
public final class DelegatingRuntimeConfigurationValidator implements RuntimeConfigurationValidator {

private final Set<RuntimeConfigurationValidator> validators;

public DelegatingRuntimeConfigurationValidator(final Set<RuntimeConfigurationValidator> validators) {
this.validators = validators;
}

@Override
public void validate(final RuntimeConfiguration configuration) {
validators.forEach(validator -> validator.validate(configuration));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* This file is part of yosql. It is subject to the license terms in the LICENSE file found in the top-level
* directory of this distribution and at https://creativecommons.org/publicdomain/zero/1.0/. No part of yosql,
* including this file, may be copied, modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/

package wtf.metio.yosql.codegen.validation;

import ch.qos.cal10n.IMessageConveyor;
import wtf.metio.yosql.codegen.exceptions.InvalidNameConfigurationException;
import wtf.metio.yosql.codegen.lifecycle.ExecutionErrors;
import wtf.metio.yosql.models.immutables.RuntimeConfiguration;

import java.util.Map;
import java.util.stream.Collectors;

import static wtf.metio.yosql.codegen.lifecycle.ApplicationErrors.NAMES_CONFIG_INVALID;

/**
* Validates {@link wtf.metio.yosql.models.immutables.NamesConfiguration}s.
*/
public final class NamesConfigurationValidator implements RuntimeConfigurationValidator {

private final ExecutionErrors errors;
private final IMessageConveyor messages;

public NamesConfigurationValidator(final ExecutionErrors errors, final IMessageConveyor messages) {
this.errors = errors;
this.messages = messages;
}

@Override
public void validate(final RuntimeConfiguration configuration) {
final var names = configuration.names();

final var counts = names.uniqueValueCount();
final var duplicates = counts.entrySet().stream()
.filter(entry -> entry.getValue() > 1)
.map(Map.Entry::getKey)
.collect(Collectors.toSet());

if (!duplicates.isEmpty()) {
errors.add(new InvalidNameConfigurationException(messages.getMessage(NAMES_CONFIG_INVALID, duplicates)));
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,12 @@
* in the LICENSE file.
*/

package wtf.metio.yosql.tooling.dagger.codegen.logging;
package wtf.metio.yosql.codegen.validation;

import wtf.metio.yosql.models.immutables.RuntimeConfiguration;

public interface RuntimeConfigurationValidator {

void validate(RuntimeConfiguration configuration);

}
Loading

0 comments on commit 01f8a20

Please sign in to comment.