diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/DefaultValidatorProvider.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/DefaultValidatorProvider.java index f3fa18ac68..835e494d3b 100644 --- a/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/DefaultValidatorProvider.java +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/DefaultValidatorProvider.java @@ -93,7 +93,7 @@ public List> createSingleEntityV } else { validators.add((SingleEntityValidator) validatorWithDependencyStatus.validator()); } - } catch (ReflectiveOperationException e) { + } catch (ReflectiveOperationException | ValidatorLoaderException e) { logger.atSevere().withCause(e).log( "Cannot instantiate validator %s", validatorClass.getCanonicalName()); } @@ -117,7 +117,7 @@ public List createSingleFileValidators( } else { validators.add(validatorWithStatus.validator()); } - } catch (ReflectiveOperationException e) { + } catch (ReflectiveOperationException | ValidatorLoaderException e) { logger.atSevere().withCause(e).log( "Cannot instantiate validator %s", validatorClass.getCanonicalName()); } @@ -139,7 +139,7 @@ public List createMultiFileValidators( } else { validators.add(validatorWithStatus.validator()); } - } catch (ReflectiveOperationException e) { + } catch (ReflectiveOperationException | ValidatorLoaderException e) { logger.atSevere().withCause(e).log( "Cannot instantiate validator %s", validatorClass.getCanonicalName()); } diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/ValidationContext.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/ValidationContext.java index a532ed4153..c39e91c2cf 100644 --- a/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/ValidationContext.java +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/ValidationContext.java @@ -16,7 +16,7 @@ package org.mobilitydata.gtfsvalidator.validator; -import com.google.auto.value.AutoValue; +import com.google.common.collect.ImmutableMap; import org.mobilitydata.gtfsvalidator.input.CountryCode; import org.mobilitydata.gtfsvalidator.input.CurrentDateTime; @@ -24,18 +24,25 @@ * A read-only context passed to particular validator objects. It gives information relevant for * validation: properties of the feed as a whole, system properties (current time) etc. */ -@AutoValue -public abstract class ValidationContext { +public class ValidationContext { public static Builder builder() { - return new AutoValue_ValidationContext.Builder(); + return new Builder(); + } + + private final ImmutableMap, Object> context; + + private ValidationContext(ImmutableMap, Object> context) { + this.context = context; } /** - * Represents a name of a GTFS feed, such as "nl-openov". + * Represents the country code of a GTFS feed, such as US or NL. * - * @return the @code{GtfsFeedName} representing the feed's name + * @return the @code{CountryCode} representing the feed's country code */ - public abstract CountryCode countryCode(); + public CountryCode countryCode() { + return get(CountryCode.class); + } /** * The time when validation started. @@ -50,27 +57,43 @@ public static Builder builder() { * * @return The time when validation started as @code{ZonedDateTime} */ - public abstract CurrentDateTime currentDateTime(); + public CurrentDateTime currentDateTime() { + return get(CurrentDateTime.class); + } /** Returns a member of the context with requested class. */ @SuppressWarnings("unchecked") public T get(Class clazz) { - if (clazz.isAssignableFrom(CountryCode.class)) { - return (T) countryCode(); - } - if (clazz.isAssignableFrom(CurrentDateTime.class)) { - return (T) currentDateTime(); + Object o = context.get(clazz); + if (o == null) { + throw new IllegalArgumentException( + "Cannot find " + clazz.getCanonicalName() + " in validation context"); } - throw new IllegalArgumentException( - "Cannot find " + clazz.getCanonicalName() + " in validation context"); + return (T) o; } - @AutoValue.Builder - public abstract static class Builder { - public abstract Builder setCountryCode(CountryCode countryCode); + /** Builder for {@link ValidationContext}. */ + public static class Builder { + private final ImmutableMap.Builder, Object> context = ImmutableMap.builder(); - public abstract Builder setCurrentDateTime(CurrentDateTime currentDateTime); + /** Sets the country code. */ + public Builder setCountryCode(CountryCode countryCode) { + return set(CountryCode.class, countryCode); + } + + /** Sets the current time. */ + public Builder setCurrentDateTime(CurrentDateTime currentDateTime) { + return set(CurrentDateTime.class, currentDateTime); + } - public abstract ValidationContext build(); + /** Sets a member of the context with requested class. */ + public Builder set(Class clazz, T obj) { + context.put(clazz, obj); + return this; + } + + public ValidationContext build() { + return new ValidationContext(context.buildOrThrow()); + } } } diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/ValidatorLoader.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/ValidatorLoader.java index 9fd3578f48..edd3ffb0a8 100644 --- a/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/ValidatorLoader.java +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/ValidatorLoader.java @@ -28,8 +28,6 @@ import java.util.Map.Entry; import javax.annotation.Nullable; import javax.inject.Inject; -import org.mobilitydata.gtfsvalidator.input.CountryCode; -import org.mobilitydata.gtfsvalidator.input.CurrentDateTime; import org.mobilitydata.gtfsvalidator.notice.NoticeContainer; import org.mobilitydata.gtfsvalidator.table.GtfsEntity; import org.mobilitydata.gtfsvalidator.table.GtfsFeedContainer; @@ -89,16 +87,7 @@ public List> getMultiFileValidators() { @SuppressWarnings("unchecked") private > void addSingleEntityValidator( - Class validatorClass) throws ValidatorLoaderException { - Constructor constructor = chooseConstructor(validatorClass); - for (Class parameterType : constructor.getParameterTypes()) { - if (!isInjectableFromContext(parameterType)) { - throw new ValidatorLoaderException( - String.format( - "Cannot inject parameter of type %s to %s constructor", - parameterType.getCanonicalName(), validatorClass.getCanonicalName())); - } - } + Class validatorClass) { for (Method method : validatorClass.getMethods()) { // A child class of SingleEntityValidator has two `validate' methods: // 1) the inherited void validate(GtfsEntity entity, NoticeContainer noticeContainer); @@ -126,20 +115,13 @@ private void addFileValidator(Class validatorClass) // Find out which GTFS tables need to be injected. List>> injectedTables = new ArrayList<>(); for (Class parameterType : constructor.getParameterTypes()) { - if (isInjectableFromContext(parameterType)) { - continue; - } if (GtfsFeedContainer.class.isAssignableFrom(parameterType)) { injectFeedContainer = true; continue; } - if (!GtfsTableContainer.class.isAssignableFrom(parameterType)) { - throw new ValidatorLoaderException( - String.format( - "Cannot inject parameter of type %s to %s constructor", - parameterType.getCanonicalName(), validatorClass.getCanonicalName())); + if (GtfsTableContainer.class.isAssignableFrom(parameterType)) { + injectedTables.add((Class>) parameterType); } - injectedTables.add((Class>) parameterType); } if (!injectFeedContainer && injectedTables.size() == 1) { @@ -149,11 +131,6 @@ private void addFileValidator(Class validatorClass) } } - private static boolean isInjectableFromContext(Class parameterType) { - return parameterType.isAssignableFrom(CurrentDateTime.class) - || parameterType.isAssignableFrom(CountryCode.class); - } - /** Chooses the default or injectable constructor. */ @SuppressWarnings("unchecked") private static Constructor chooseConstructor(Class validatorClass) @@ -175,7 +152,8 @@ private static Constructor chooseConstructor(Class validatorClass) * information in the returned {@link ValidatorWithDependencyStatus}. */ private static ValidatorWithDependencyStatus createValidator( - Class clazz, DependencyResolver dependencyResolver) throws ReflectiveOperationException { + Class clazz, DependencyResolver dependencyResolver) + throws ReflectiveOperationException, ValidatorLoaderException { Constructor chosenConstructor; try { chosenConstructor = chooseConstructor(clazz); @@ -186,8 +164,16 @@ private static ValidatorWithDependencyStatus createValidator( // Inject constructor parameters. Object[] parameters = new Object[chosenConstructor.getParameterCount()]; for (int i = 0; i < parameters.length; ++i) { - parameters[i] = - dependencyResolver.resolveDependency(chosenConstructor.getParameters()[i].getType()); + Class parameterType = chosenConstructor.getParameters()[i].getType(); + try { + parameters[i] = dependencyResolver.resolveDependency(parameterType); + } catch (IllegalArgumentException e) { + throw new ValidatorLoaderException( + String.format( + "Cannot inject parameter of type %s to %s constructor", + parameterType.getCanonicalName(), clazz.getCanonicalName()), + e); + } } chosenConstructor.setAccessible(true); T validator = chosenConstructor.newInstance(parameters); @@ -205,7 +191,8 @@ private static ValidatorWithDependencyStatus createValidator( */ public static ValidatorWithDependencyStatus createValidatorWithContext( - Class clazz, ValidationContext validationContext) throws ReflectiveOperationException { + Class clazz, ValidationContext validationContext) + throws ReflectiveOperationException, ValidatorLoaderException { return (ValidatorWithDependencyStatus) createValidator(clazz, new DependencyResolver(validationContext, null, null)); } @@ -216,7 +203,7 @@ ValidatorWithDependencyStatus createSingleFileValidator( Class clazz, GtfsTableContainer table, ValidationContext validationContext) - throws ReflectiveOperationException { + throws ReflectiveOperationException, ValidatorLoaderException { return (ValidatorWithDependencyStatus) createValidator(clazz, new DependencyResolver(validationContext, table, null)); } @@ -225,7 +212,7 @@ ValidatorWithDependencyStatus createSingleFileValidator( @SuppressWarnings("unchecked") public static ValidatorWithDependencyStatus createMultiFileValidator( Class clazz, GtfsFeedContainer feed, ValidationContext validationContext) - throws ReflectiveOperationException { + throws ReflectiveOperationException, ValidatorLoaderException { return createValidator(clazz, new DependencyResolver(validationContext, null, feed)); } diff --git a/core/src/test/java/org/mobilitydata/gtfsvalidator/validator/ValidationContextTest.java b/core/src/test/java/org/mobilitydata/gtfsvalidator/validator/ValidationContextTest.java index 3ab46ff417..3270fb70f2 100644 --- a/core/src/test/java/org/mobilitydata/gtfsvalidator/validator/ValidationContextTest.java +++ b/core/src/test/java/org/mobilitydata/gtfsvalidator/validator/ValidationContextTest.java @@ -54,6 +54,16 @@ public void get_unsupported_throws() { IllegalArgumentException.class, () -> VALIDATION_CONTEXT.get(ChildCurrentDateTime.class)); } + @Test + public void get_extraIntegerObject_successful() { + assertThat( + ValidationContext.builder() + .set(Integer.class, new Integer(10)) + .build() + .get(Integer.class)) + .isEqualTo(10); + } + private static class ChildCurrentDateTime extends CurrentDateTime { public ChildCurrentDateTime(ZonedDateTime now) { diff --git a/core/src/test/java/org/mobilitydata/gtfsvalidator/validator/ValidatorLoaderTest.java b/core/src/test/java/org/mobilitydata/gtfsvalidator/validator/ValidatorLoaderTest.java index 66b64fc632..55edfd49af 100644 --- a/core/src/test/java/org/mobilitydata/gtfsvalidator/validator/ValidatorLoaderTest.java +++ b/core/src/test/java/org/mobilitydata/gtfsvalidator/validator/ValidatorLoaderTest.java @@ -43,7 +43,8 @@ public class ValidatorLoaderTest { .build(); @Test - public void createValidatorWithContext_injectsContext() throws ReflectiveOperationException { + public void createValidatorWithContext_injectsContext() + throws ReflectiveOperationException, ValidatorLoaderException { GtfsTestEntityValidator validator = ValidatorLoader.createValidatorWithContext( GtfsTestEntityValidator.class, VALIDATION_CONTEXT) @@ -55,7 +56,7 @@ public void createValidatorWithContext_injectsContext() throws ReflectiveOperati @Test public void createSingleFileValidator_injectsTableContainerAndContext() - throws ReflectiveOperationException { + throws ReflectiveOperationException, ValidatorLoaderException { GtfsTestTableContainer table = new GtfsTestTableContainer(TableStatus.EMPTY_FILE); GtfsTestSingleFileValidator validator = (GtfsTestSingleFileValidator) @@ -70,7 +71,7 @@ public void createSingleFileValidator_injectsTableContainerAndContext() @Test public void createMultiFileValidator_injectsFeedContainerAndContext() - throws ReflectiveOperationException { + throws ReflectiveOperationException, ValidatorLoaderException { GtfsTestTableContainer stopTable = new GtfsTestTableContainer(TableStatus.PARSABLE_HEADERS_AND_ROWS); GtfsFeedContainer feedContainer = new GtfsFeedContainer(ImmutableList.of(stopTable)); @@ -88,7 +89,7 @@ public void createMultiFileValidator_injectsFeedContainerAndContext() @Test public void createMultiFileValidator_singleContainer_dependenciesHaveErrors() - throws ReflectiveOperationException { + throws ReflectiveOperationException, ValidatorLoaderException { GtfsTestTableContainer table = new GtfsTestTableContainer(TableStatus.UNPARSABLE_ROWS); GtfsFeedContainer feedContainer = new GtfsFeedContainer(ImmutableList.of(table));