diff --git a/.gitignore b/.gitignore index af4b5e8a58..d9f2809763 100644 --- a/.gitignore +++ b/.gitignore @@ -104,3 +104,4 @@ app/pkg/bin/ processor/notices/bin/ processor/notices/tests/bin/ web/service/bin/ +/web/service/execution_result.json diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/table/AnyTableLoader.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/AnyTableLoader.java index 3765bf22cb..13a03c75fa 100644 --- a/core/src/main/java/org/mobilitydata/gtfsvalidator/table/AnyTableLoader.java +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/AnyTableLoader.java @@ -61,13 +61,11 @@ public static GtfsTableContainer load( csvFile = new CsvFile(csvInputStream, gtfsFilename, settings); } catch (TextParsingException e) { noticeContainer.addValidationNotice(new CsvParsingFailedNotice(gtfsFilename, e)); - return tableDescriptor.createContainerForInvalidStatus( - GtfsTableContainer.TableStatus.INVALID_HEADERS); + return tableDescriptor.createContainerForInvalidStatus(TableStatus.INVALID_HEADERS); } if (csvFile.isEmpty()) { noticeContainer.addValidationNotice(new EmptyFileNotice(gtfsFilename)); - return tableDescriptor.createContainerForInvalidStatus( - GtfsTableContainer.TableStatus.EMPTY_FILE); + return tableDescriptor.createContainerForInvalidStatus(TableStatus.EMPTY_FILE); } final CsvHeader header = csvFile.getHeader(); final ImmutableList columnDescriptors = tableDescriptor.getColumns(); @@ -75,8 +73,7 @@ public static GtfsTableContainer load( validateHeaders(validatorProvider, gtfsFilename, header, columnDescriptors); noticeContainer.addAll(headerNotices); if (headerNotices.hasValidationErrors()) { - return tableDescriptor.createContainerForInvalidStatus( - GtfsTableContainer.TableStatus.INVALID_HEADERS); + return tableDescriptor.createContainerForInvalidStatus(TableStatus.INVALID_HEADERS); } final int nColumns = columnDescriptors.size(); final ImmutableMap fieldLoadersMap = tableDescriptor.getFieldLoaders(); @@ -133,15 +130,13 @@ public static GtfsTableContainer load( } } catch (TextParsingException e) { noticeContainer.addValidationNotice(new CsvParsingFailedNotice(gtfsFilename, e)); - return tableDescriptor.createContainerForInvalidStatus( - GtfsTableContainer.TableStatus.UNPARSABLE_ROWS); + return tableDescriptor.createContainerForInvalidStatus(TableStatus.UNPARSABLE_ROWS); } finally { logFieldCacheStats(gtfsFilename, fieldCaches, columnDescriptors); } if (hasUnparsableRows) { logger.atSevere().log("Failed to parse some rows in %s", gtfsFilename); - return tableDescriptor.createContainerForInvalidStatus( - GtfsTableContainer.TableStatus.UNPARSABLE_ROWS); + return tableDescriptor.createContainerForInvalidStatus(TableStatus.UNPARSABLE_ROWS); } GtfsTableContainer table = tableDescriptor.createContainerForHeaderAndEntities(header, entities, noticeContainer); @@ -197,14 +192,12 @@ private static void logFieldCacheStats( } } - public static GtfsTableContainer loadMissingFile( - GtfsTableDescriptor tableDescriptor, + public static GtfsContainer loadMissingFile( + GtfsDescriptor tableDescriptor, ValidatorProvider validatorProvider, NoticeContainer noticeContainer) { String gtfsFilename = tableDescriptor.gtfsFilename(); - GtfsTableContainer table = - tableDescriptor.createContainerForInvalidStatus( - GtfsTableContainer.TableStatus.MISSING_FILE); + GtfsContainer table = tableDescriptor.createContainerForInvalidStatus(TableStatus.MISSING_FILE); if (tableDescriptor.isRecommended()) { noticeContainer.addValidationNotice(new MissingRecommendedFileNotice(gtfsFilename)); } diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsContainer.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsContainer.java new file mode 100644 index 0000000000..c5ab7ce385 --- /dev/null +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsContainer.java @@ -0,0 +1,50 @@ +package org.mobilitydata.gtfsvalidator.table; + +import java.util.List; +import java.util.Optional; + +public abstract class GtfsContainer { + + private final D descriptor; + private final TableStatus tableStatus; + + public GtfsContainer(D descriptor, TableStatus tableStatus) { + this.tableStatus = tableStatus; + this.descriptor = descriptor; + } + + public TableStatus getTableStatus() { + return tableStatus; + } + + public D getDescriptor() { + return descriptor; + } + + public abstract Class getEntityClass(); + + public int entityCount() { + return getEntities().size(); + } + + public abstract List getEntities(); + + public abstract String gtfsFilename(); + + public abstract Optional byTranslationKey(String recordId, String recordSubId); + + public boolean isMissingFile() { + return tableStatus == TableStatus.MISSING_FILE; + } + + public boolean isParsedSuccessfully() { + switch (tableStatus) { + case PARSABLE_HEADERS_AND_ROWS: + return true; + case MISSING_FILE: + return !descriptor.isRequired(); + default: + return false; + } + } +} diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsDescriptor.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsDescriptor.java new file mode 100644 index 0000000000..f1de71d042 --- /dev/null +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsDescriptor.java @@ -0,0 +1,27 @@ +package org.mobilitydata.gtfsvalidator.table; + +// TODO: review class name maybe GtfsFileDescriptor +public abstract class GtfsDescriptor { + + public abstract C createContainerForInvalidStatus( + TableStatus tableStatus); + + // True if the specified file is required in a feed. + private boolean required; + + private TableStatus tableStatus; + + public abstract boolean isRecommended(); + + public abstract Class getEntityClass(); + + public abstract String gtfsFilename(); + + public boolean isRequired() { + return this.required; + } + + public void setRequired(boolean required) { + this.required = required; + } +} diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsFeedContainer.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsFeedContainer.java index 9b96d3d68d..142d6b82d9 100644 --- a/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsFeedContainer.java +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsFeedContainer.java @@ -18,20 +18,19 @@ import com.google.common.base.Ascii; import java.util.*; -import org.mobilitydata.gtfsvalidator.table.GtfsTableContainer.TableStatus; /** * Container for a whole parsed GTFS feed with all its tables. * - *

The tables are kept as {@code GtfsTableContainer} instances. + *

The tables are kept as {@code GtfsContainer} instances. */ public class GtfsFeedContainer { - private final Map> tables = new HashMap<>(); - private final Map, GtfsTableContainer> tablesByClass = + private final Map> tables = new HashMap<>(); + private final Map, GtfsContainer> tablesByClass = new HashMap<>(); - public GtfsFeedContainer(List> tableContainerList) { - for (GtfsTableContainer table : tableContainerList) { + public GtfsFeedContainer(List> tableContainerList) { + for (GtfsContainer table : tableContainerList) { tables.put(table.gtfsFilename(), table); tablesByClass.put(table.getClass(), table); } @@ -49,11 +48,12 @@ public GtfsFeedContainer(List> tableContainerList) { * @param filename file name, including ".txt" extension * @return GTFS table or empty if the table is not supported by schema */ - public Optional> getTableForFilename(String filename) { - return Optional.ofNullable(tables.getOrDefault(Ascii.toLowerCase(filename), null)); + public > Optional getTableForFilename(String filename) { + return (Optional) + Optional.ofNullable(tables.getOrDefault(Ascii.toLowerCase(filename), null)); } - public > T getTable(Class clazz) { + public > T getTable(Class clazz) { return (T) tablesByClass.get(clazz); } @@ -65,7 +65,7 @@ public > T getTable(Class clazz) { * @return true if all files were successfully parsed, false otherwise */ public boolean isParsedSuccessfully() { - for (GtfsTableContainer table : tables.values()) { + for (GtfsContainer table : tables.values()) { if (!table.isParsedSuccessfully()) { return false; } @@ -73,13 +73,13 @@ public boolean isParsedSuccessfully() { return true; } - public Collection> getTables() { + public Collection> getTables() { return tables.values(); } public String tableTotalsText() { List totalList = new ArrayList<>(); - for (GtfsTableContainer table : tables.values()) { + for (GtfsContainer table : tables.values()) { if (table.getTableStatus() == TableStatus.MISSING_FILE && !table.getDescriptor().isRequired()) { continue; diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsFeedLoader.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsFeedLoader.java index 1edb051184..9223b387ac 100644 --- a/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsFeedLoader.java +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsFeedLoader.java @@ -19,6 +19,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.flogger.FluentLogger; import java.io.InputStream; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -46,7 +47,7 @@ */ public class GtfsFeedLoader { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); - private final HashMap> tableDescriptors = new HashMap<>(); + private final HashMap> tableDescriptors = new HashMap<>(); private int numThreads = 1; /** @@ -56,12 +57,15 @@ public class GtfsFeedLoader { private final List> multiFileValidatorsWithParsingErrors = new ArrayList<>(); - public GtfsFeedLoader( - ImmutableList>> tableDescriptorClasses) { - for (Class> clazz : tableDescriptorClasses) { - GtfsTableDescriptor descriptor; + public GtfsFeedLoader(ImmutableList>> tableDescriptorClasses) { + for (Class> clazz : tableDescriptorClasses) { + GtfsDescriptor descriptor; try { - descriptor = clazz.asSubclass(GtfsTableDescriptor.class).getConstructor().newInstance(); + // Skipping abstract classes. Example: GtfsTableDescriptor. + if (Modifier.isAbstract(clazz.getModifiers())) { + continue; + } + descriptor = clazz.asSubclass(GtfsDescriptor.class).getConstructor().newInstance(); } catch (ReflectiveOperationException e) { logger.atSevere().withCause(e).log( "Possible bug in GTFS annotation processor: expected a constructor without parameters" @@ -73,7 +77,7 @@ public GtfsFeedLoader( } } - public Collection> getTableDescriptors() { + public Collection> getTableDescriptors() { return Collections.unmodifiableCollection(tableDescriptors.values()); } @@ -100,19 +104,36 @@ public GtfsFeedContainer loadAndValidate( Map> remainingDescriptors = (Map>) tableDescriptors.clone(); for (String filename : gtfsInput.getFilenames()) { - GtfsTableDescriptor tableDescriptor = remainingDescriptors.remove(filename.toLowerCase()); + GtfsDescriptor tableDescriptor = remainingDescriptors.remove(filename.toLowerCase()); if (tableDescriptor == null) { noticeContainer.addValidationNotice(new UnknownFileNotice(filename)); } else { loaderCallables.add( () -> { NoticeContainer loaderNotices = new NoticeContainer(); - GtfsTableContainer tableContainer; + GtfsContainer tableContainer; try (InputStream inputStream = gtfsInput.getFile(filename)) { try { - tableContainer = - AnyTableLoader.load( - tableDescriptor, validatorProvider, inputStream, loaderNotices); + if (tableDescriptor instanceof GtfsTableDescriptor) { + tableContainer = + AnyTableLoader.load( + (GtfsTableDescriptor) tableDescriptor, + validatorProvider, + inputStream, + loaderNotices); + } else if (tableDescriptor instanceof GtfsJsonDescriptor) { + tableContainer = + JsonFileLoader.load( + (GtfsJsonDescriptor) tableDescriptor, + validatorProvider, + inputStream, + loaderNotices); + } else { + logger.atSevere().log( + "Runtime exception table descriptor not supported: %s", + tableDescriptor.getClass().getName()); + throw new RuntimeException("Table descriptor is not a supported type"); + } } catch (RuntimeException e) { // This handler should prevent ExecutionException for // this thread. We catch an exception here for storing @@ -130,11 +151,10 @@ public GtfsFeedContainer loadAndValidate( }); } } - ArrayList> tableContainers = new ArrayList<>(); + ArrayList> tableContainers = new ArrayList<>(); tableContainers.ensureCapacity(tableDescriptors.size()); - for (GtfsTableDescriptor tableDescriptor : remainingDescriptors.values()) { - tableContainers.add( - AnyTableLoader.loadMissingFile(tableDescriptor, validatorProvider, noticeContainer)); + for (GtfsDescriptor tableDescriptor : remainingDescriptors.values()) { + tableContainers.add(AnyTableLoader.loadMissingFile(tableDescriptor, validatorProvider, noticeContainer)); } try { for (Future futureContainer : exec.invokeAll(loaderCallables)) { @@ -186,11 +206,10 @@ private static void addThreadExecutionError( } static class TableAndNoticeContainers { - final GtfsTableContainer tableContainer; + final GtfsContainer tableContainer; final NoticeContainer noticeContainer; - public TableAndNoticeContainers( - GtfsTableContainer tableContainer, NoticeContainer noticeContainer) { + public TableAndNoticeContainers(GtfsContainer tableContainer, NoticeContainer noticeContainer) { this.tableContainer = tableContainer; this.noticeContainer = noticeContainer; } diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsJsonContainer.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsJsonContainer.java new file mode 100644 index 0000000000..d02c2d4dcc --- /dev/null +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsJsonContainer.java @@ -0,0 +1,9 @@ +package org.mobilitydata.gtfsvalidator.table; + +public abstract class GtfsJsonContainer> + extends GtfsContainer { + + public GtfsJsonContainer(D descriptor, TableStatus tableStatus) { + super(descriptor, tableStatus); + } +} diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsJsonDescriptor.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsJsonDescriptor.java new file mode 100644 index 0000000000..e3cc6b63d8 --- /dev/null +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsJsonDescriptor.java @@ -0,0 +1,10 @@ +package org.mobilitydata.gtfsvalidator.table; + +import java.util.List; +import org.mobilitydata.gtfsvalidator.notice.NoticeContainer; + +public abstract class GtfsJsonDescriptor extends GtfsDescriptor { + + public abstract GtfsJsonContainer createContainerForEntities( + List entities, NoticeContainer noticeContainer); +} diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsTableContainer.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsTableContainer.java index 010da6a1da..c94ba0f7fb 100644 --- a/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsTableContainer.java +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsTableContainer.java @@ -31,29 +31,16 @@ * * @param subclass of {@code GtfsEntity} */ -public abstract class GtfsTableContainer { - - private final GtfsTableDescriptor descriptor; - - private final TableStatus tableStatus; +public abstract class GtfsTableContainer + extends GtfsContainer { private final CsvHeader header; - public GtfsTableContainer( - GtfsTableDescriptor descriptor, TableStatus tableStatus, CsvHeader header) { - this.descriptor = descriptor; - this.tableStatus = tableStatus; + public GtfsTableContainer(D descriptor, TableStatus tableStatus, CsvHeader header) { + super(descriptor, tableStatus); this.header = header; } - public GtfsTableDescriptor getDescriptor() { - return descriptor; - } - - public TableStatus getTableStatus() { - return tableStatus; - } - public CsvHeader getHeader() { return header; } @@ -94,74 +81,4 @@ public boolean hasColumn(String columnName) { * @return entity with the given translation record id, if any */ public abstract Optional byTranslationKey(String recordId, String recordSubId); - - /** - * Tells if the file is missing. - * - * @return true if the file is missing, false otherwise - */ - public boolean isMissingFile() { - return tableStatus == TableStatus.MISSING_FILE; - } - - /** - * Tells if the file was successfully parsed. - * - *

If all files in the feed were successfully parsed, then file validators may be executed. - * - *

A successfully parsed file must meet the following conditions: - * - *

    - *
  • the file was successfully parsed as CSV; - *
  • all headers are valid, required headers are present; - *
  • all rows are successfully parsed; - *
  • if the file is required, it is present in the feed. - *
- * - * @return true if file was successfully parsed, false otherwise - */ - public boolean isParsedSuccessfully() { - switch (tableStatus) { - case PARSABLE_HEADERS_AND_ROWS: - return true; - case MISSING_FILE: - return !descriptor.isRequired(); - default: - return false; - } - } - - /** - * Status of loading this table. This is includes parsing of the CSV file and validation of the - * single file, but does not include any cross-file validations. - */ - public enum TableStatus { - /** The file is completely empty, i.e. it has no rows and even no headers. */ - EMPTY_FILE, - - /** The file is missing in the GTFS feed. */ - MISSING_FILE, - - /** The file was parsed successfully. It has headers and 0, 1 or many rows. */ - PARSABLE_HEADERS_AND_ROWS, - - /** - * The file has invalid headers, e.g., they failed to parse or some required headers are - * missing. The other rows were not scanned. - * - *

Note that unknown headers are not considered invalid. - */ - INVALID_HEADERS, - - /** - * Some of the rows failed to parse, e.g., they have missing required fields or invalid field - * values. - * - *

However, the headers are valid. - * - *

This does not include cross-file or cross-row validation. This also does not include - * single-entity validation. - */ - UNPARSABLE_ROWS, - } } diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsTableDescriptor.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsTableDescriptor.java index 01f3b773db..74a2f76a57 100644 --- a/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsTableDescriptor.java +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsTableDescriptor.java @@ -7,35 +7,18 @@ import org.mobilitydata.gtfsvalidator.notice.NoticeContainer; import org.mobilitydata.gtfsvalidator.parsing.CsvHeader; -public abstract class GtfsTableDescriptor { +public abstract class GtfsTableDescriptor extends GtfsDescriptor { - // True if the specified file is required in a feed. - private boolean required; - - public abstract GtfsTableContainer createContainerForInvalidStatus( - GtfsTableContainer.TableStatus tableStatus); + @Override + public abstract GtfsTableContainer createContainerForInvalidStatus(TableStatus tableStatus); public abstract GtfsTableContainer createContainerForHeaderAndEntities( CsvHeader header, List entities, NoticeContainer noticeContainer); public abstract GtfsEntityBuilder createEntityBuilder(); - public abstract Class getEntityClass(); - - public abstract String gtfsFilename(); - public abstract ImmutableMap getFieldLoaders(); - public abstract boolean isRecommended(); - - public boolean isRequired() { - return this.required; - } - - public void setRequired(boolean required) { - this.required = required; - } - public abstract Optional maxCharsPerColumn(); public abstract ImmutableList getColumns(); diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/table/JsonFileLoader.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/JsonFileLoader.java new file mode 100644 index 0000000000..f69bba7c69 --- /dev/null +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/JsonFileLoader.java @@ -0,0 +1,24 @@ +package org.mobilitydata.gtfsvalidator.table; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import org.mobilitydata.gtfsvalidator.notice.NoticeContainer; +import org.mobilitydata.gtfsvalidator.validator.ValidatorProvider; + +public class JsonFileLoader { + + public static GtfsJsonContainer load( + GtfsJsonDescriptor tableDescriptor, + ValidatorProvider validatorProvider, + InputStream inputStream, + NoticeContainer noticeContainer) { + final List entities = new ArrayList<>(); + GtfsJsonContainer table = tableDescriptor.createContainerForEntities(entities, noticeContainer); + // ValidatorUtil.invokeSingleFileValidators( + // validatorProvider.createSingleFileValidators( + // table, singleFileValidatorsWithParsingErrors::add), + // noticeContainer); + return table; + } +} diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/table/TableStatus.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/TableStatus.java new file mode 100644 index 0000000000..417d01e772 --- /dev/null +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/TableStatus.java @@ -0,0 +1,35 @@ +package org.mobilitydata.gtfsvalidator.table; + +/** + * Status of loading this table. This is includes parsing of the CSV file and validation of the + * single file, but does not include any cross-file validations. + */ +public enum TableStatus { + /** The file is completely empty, i.e. it has no rows and even no headers. */ + EMPTY_FILE, + + /** The file is missing in the GTFS feed. */ + MISSING_FILE, + + /** The file was parsed successfully. It has headers and 0, 1 or many rows. */ + PARSABLE_HEADERS_AND_ROWS, + + /** + * The file has invalid headers, e.g., they failed to parse or some required headers are missing. + * The other rows were not scanned. + * + *

Note that unknown headers are not considered invalid. + */ + INVALID_HEADERS, + + /** + * Some of the rows failed to parse, e.g., they have missing required fields or invalid field + * values. + * + *

However, the headers are valid. + * + *

This does not include cross-file or cross-row validation. This also does not include + * single-entity validation. + */ + UNPARSABLE_ROWS, +} diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/testing/LoadingHelper.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/testing/LoadingHelper.java index b51f788eb6..1fefbfaa39 100644 --- a/core/src/main/java/org/mobilitydata/gtfsvalidator/testing/LoadingHelper.java +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/testing/LoadingHelper.java @@ -58,7 +58,7 @@ public void setValidatorLoader(ValidatorLoader validatorLoader) { this.validatorLoader = validatorLoader; } - public > Y load( + public > Y load( GtfsTableDescriptor tableDescriptor, String... lines) throws ValidatorLoaderException { String content = Arrays.stream(lines).collect(Collectors.joining("\n")); InputStream in = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)); diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/ClassGraphDiscovery.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/ClassGraphDiscovery.java index f435c3e079..170305eedc 100644 --- a/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/ClassGraphDiscovery.java +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/ClassGraphDiscovery.java @@ -7,7 +7,7 @@ import java.util.List; import org.mobilitydata.gtfsvalidator.annotation.GtfsValidator; import org.mobilitydata.gtfsvalidator.notice.Notice; -import org.mobilitydata.gtfsvalidator.table.GtfsTableDescriptor; +import org.mobilitydata.gtfsvalidator.table.GtfsDescriptor; /** Discovers GTFS table descriptor and validator classes in the given Java packages. */ public class ClassGraphDiscovery { @@ -23,8 +23,8 @@ private ClassGraphDiscovery() {} /** Discovers GtfsTableDescriptor subclasses in the default table package. */ @SuppressWarnings("unchecked") - public static ImmutableList>> discoverTables() { - ImmutableList.Builder>> tableDescriptors = + public static ImmutableList>> discoverTables() { + ImmutableList.Builder>> tableDescriptors = ImmutableList.builder(); try (ScanResult scanResult = new ClassGraph() @@ -32,8 +32,8 @@ public static ImmutableList>> discoverTab .enableAnnotationInfo() .acceptPackages(DEFAULT_TABLE_PACKAGE) .scan()) { - for (ClassInfo classInfo : scanResult.getSubclasses(GtfsTableDescriptor.class)) { - tableDescriptors.add((Class>) classInfo.loadClass()); + for (ClassInfo classInfo : scanResult.getSubclasses(GtfsDescriptor.class)) { + tableDescriptors.add((Class>) classInfo.loadClass()); } } return tableDescriptors.build(); 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 835e494d3b..5af7d70904 100644 --- a/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/DefaultValidatorProvider.java +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/DefaultValidatorProvider.java @@ -21,9 +21,7 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; -import org.mobilitydata.gtfsvalidator.table.GtfsEntity; -import org.mobilitydata.gtfsvalidator.table.GtfsFeedContainer; -import org.mobilitydata.gtfsvalidator.table.GtfsTableContainer; +import org.mobilitydata.gtfsvalidator.table.*; import org.mobilitydata.gtfsvalidator.validator.ValidatorLoader.ValidatorWithDependencyStatus; /** Default implementation of {@link ValidatorProvider}. */ @@ -35,7 +33,7 @@ public class DefaultValidatorProvider implements ValidatorProvider { private final TableHeaderValidator tableHeaderValidator; private final ListMultimap, Class>> singleEntityValidators; - private final ListMultimap>, Class> + private final ListMultimap>, Class> singleFileValidators; private final List> multiFileValidators; @@ -103,12 +101,13 @@ public List> createSingleEntityV @Override @SuppressWarnings("unchecked") - public List createSingleFileValidators( - GtfsTableContainer table, - Consumer> validatorsWithParsingErrors) { + public + List createSingleFileValidators( + GtfsContainer table, + Consumer> validatorsWithParsingErrors) { List validators = new ArrayList<>(); for (Class validatorClass : - singleFileValidators.get((Class>) table.getClass())) { + singleFileValidators.get((Class>) table.getClass())) { try { ValidatorWithDependencyStatus validatorWithStatus = ValidatorLoader.createSingleFileValidator(validatorClass, table, validationContext); 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 edd3ffb0a8..43e089d569 100644 --- a/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/ValidatorLoader.java +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/ValidatorLoader.java @@ -29,9 +29,9 @@ import javax.annotation.Nullable; import javax.inject.Inject; import org.mobilitydata.gtfsvalidator.notice.NoticeContainer; +import org.mobilitydata.gtfsvalidator.table.GtfsContainer; import org.mobilitydata.gtfsvalidator.table.GtfsEntity; import org.mobilitydata.gtfsvalidator.table.GtfsFeedContainer; -import org.mobilitydata.gtfsvalidator.table.GtfsTableContainer; /** * A {@code ValidatorLoader} object locates all validators registered with {@code @GtfsValidator} @@ -43,7 +43,7 @@ public class ValidatorLoader { private final ListMultimap, Class>> singleEntityValidators = ArrayListMultimap.create(); - private final ListMultimap>, Class> + private final ListMultimap>, Class> singleFileValidators = ArrayListMultimap.create(); private final List> multiFileValidators = new ArrayList<>(); @@ -75,7 +75,7 @@ private ValidatorLoader() {} } /** Loaded single-file validator classes keyed by table container class. */ - public ListMultimap>, Class> + public ListMultimap>, Class> getSingleFileValidators() { return singleFileValidators; } @@ -113,14 +113,14 @@ private void addFileValidator(Class validatorClass) // Indicates that the full GtfsFeedContainer needs to be injected. boolean injectFeedContainer = false; // Find out which GTFS tables need to be injected. - List>> injectedTables = new ArrayList<>(); + List>> injectedTables = new ArrayList<>(); for (Class parameterType : constructor.getParameterTypes()) { if (GtfsFeedContainer.class.isAssignableFrom(parameterType)) { injectFeedContainer = true; continue; } - if (GtfsTableContainer.class.isAssignableFrom(parameterType)) { - injectedTables.add((Class>) parameterType); + if (GtfsContainer.class.isAssignableFrom(parameterType)) { + injectedTables.add((Class>) parameterType); } } @@ -201,7 +201,7 @@ ValidatorWithDependencyStatus createValidatorWithContext( public static ValidatorWithDependencyStatus createSingleFileValidator( Class clazz, - GtfsTableContainer table, + GtfsContainer table, ValidationContext validationContext) throws ReflectiveOperationException, ValidatorLoaderException { return (ValidatorWithDependencyStatus) @@ -222,7 +222,7 @@ public static ValidatorWithDependencyStatus createM */ private static class DependencyResolver { private final ValidationContext context; - @Nullable private final GtfsTableContainer tableContainer; + @Nullable private final GtfsContainer tableContainer; @Nullable private final GtfsFeedContainer feedContainer; /** This will be set to true if a resolved dependency was not parsed successfully. */ @@ -230,7 +230,7 @@ private static class DependencyResolver { public DependencyResolver( ValidationContext context, - @Nullable GtfsTableContainer tableContainer, + @Nullable GtfsContainer tableContainer, @Nullable GtfsFeedContainer feedContainer) { this.context = context; this.tableContainer = tableContainer; @@ -257,9 +257,9 @@ public Object resolveDependency(Class parameterClass) { } return tableContainer; } - if (feedContainer != null && GtfsTableContainer.class.isAssignableFrom(parameterClass)) { - GtfsTableContainer container = - feedContainer.getTable((Class>) parameterClass); + if (feedContainer != null && GtfsContainer.class.isAssignableFrom(parameterClass)) { + GtfsContainer container = + feedContainer.getTable((Class>) parameterClass); if (container != null && !container.isParsedSuccessfully()) { dependenciesHaveErrors = true; } @@ -305,7 +305,7 @@ public String listValidators() { if (!singleFileValidators.isEmpty()) { builder.append("Single-file validators\n"); for (Map.Entry< - Class>, Collection>> + Class>, Collection>> entry : singleFileValidators.asMap().entrySet()) { builder.append("\t").append(entry.getKey().getSimpleName()).append(": "); for (Class validatorClass : entry.getValue()) { diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/ValidatorProvider.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/ValidatorProvider.java index 2c243a8dc9..9e60330059 100644 --- a/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/ValidatorProvider.java +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/ValidatorProvider.java @@ -18,9 +18,7 @@ import java.util.List; import java.util.function.Consumer; -import org.mobilitydata.gtfsvalidator.table.GtfsEntity; -import org.mobilitydata.gtfsvalidator.table.GtfsFeedContainer; -import org.mobilitydata.gtfsvalidator.table.GtfsTableContainer; +import org.mobilitydata.gtfsvalidator.table.*; /** * Provider of all kinds of validators for fields, entities and files. @@ -57,9 +55,10 @@ List> createSingleEntityValidato * @param table GTFS table to validate * @param type of the GTFS entity */ - List createSingleFileValidators( - GtfsTableContainer table, - Consumer> validatorsWithParsingErrors); + + List createSingleFileValidators( + GtfsContainer table, + Consumer> validatorsWithParsingErrors); /** * Creates a list of cross-table validators. Any validator that has a dependency with parse errors diff --git a/core/src/test/java/org/mobilitydata/gtfsvalidator/table/AnyTableLoaderTest.java b/core/src/test/java/org/mobilitydata/gtfsvalidator/table/AnyTableLoaderTest.java index 6c6da42f6e..5dee4d9aca 100644 --- a/core/src/test/java/org/mobilitydata/gtfsvalidator/table/AnyTableLoaderTest.java +++ b/core/src/test/java/org/mobilitydata/gtfsvalidator/table/AnyTableLoaderTest.java @@ -46,8 +46,7 @@ public void setup() { public void invalidInputStream() { var testTableDescriptor = mock(GtfsTableDescriptor.class); when(testTableDescriptor.gtfsFilename()).thenReturn("_not_a_valid_file_"); - when(testTableDescriptor.createContainerForInvalidStatus( - GtfsTableContainer.TableStatus.INVALID_HEADERS)) + when(testTableDescriptor.createContainerForInvalidStatus(TableStatus.INVALID_HEADERS)) .thenReturn(mockContainer); var loadedContainer = @@ -61,8 +60,7 @@ public void invalidInputStream() { public void emptyInputStream() { var testTableDescriptor = mock(GtfsTableDescriptor.class); when(testTableDescriptor.gtfsFilename()).thenReturn("filename"); - when(testTableDescriptor.createContainerForInvalidStatus( - GtfsTableContainer.TableStatus.EMPTY_FILE)) + when(testTableDescriptor.createContainerForInvalidStatus(TableStatus.EMPTY_FILE)) .thenReturn(mockContainer); InputStream csvInputStream = toInputStream(""); @@ -79,8 +77,7 @@ public void invalidHeaders() { var testTableDescriptor = mock(GtfsTableDescriptor.class); when(testTableDescriptor.gtfsFilename()).thenReturn("filename"); when(testTableDescriptor.getColumns()).thenReturn(ImmutableList.of()); - when(testTableDescriptor.createContainerForInvalidStatus( - GtfsTableContainer.TableStatus.INVALID_HEADERS)) + when(testTableDescriptor.createContainerForInvalidStatus(TableStatus.INVALID_HEADERS)) .thenReturn(mockContainer); InputStream csvInputStream = toInputStream("A file with no headers"); ValidationNotice headerValidationNotice = new EmptyColumnNameNotice("stops.txt", 0); @@ -109,8 +106,7 @@ public void validate( @Test public void invalidRowLengthNotice() { var testTableDescriptor = spy(new GtfsTestTableDescriptor()); - when(testTableDescriptor.createContainerForInvalidStatus( - GtfsTableContainer.TableStatus.UNPARSABLE_ROWS)) + when(testTableDescriptor.createContainerForInvalidStatus(TableStatus.UNPARSABLE_ROWS)) .thenReturn(mockContainer); when(validatorProvider.getTableHeaderValidator()).thenReturn(mock(TableHeaderValidator.class)); InputStream inputStream = toInputStream("id,code\n" + "s1\n"); @@ -138,8 +134,7 @@ public void parsableTableRows() { var loadedContainer = AnyTableLoader.load(testTableDescriptor, validatorProvider, inputStream, loaderNotices); - assertThat(loadedContainer.getTableStatus()) - .isEqualTo(GtfsTableContainer.TableStatus.PARSABLE_HEADERS_AND_ROWS); + assertThat(loadedContainer.getTableStatus()).isEqualTo(TableStatus.PARSABLE_HEADERS_AND_ROWS); verify(validator, times(1)).validate(any()); } @@ -165,8 +160,7 @@ public void missingRequiredField() { .setIsMixedCase(false) .setIsCached(false) .build())); - when(testTableDescriptor.createContainerForInvalidStatus( - GtfsTableContainer.TableStatus.UNPARSABLE_ROWS)) + when(testTableDescriptor.createContainerForInvalidStatus(TableStatus.UNPARSABLE_ROWS)) .thenReturn(mockContainer); when(validatorProvider.getTableHeaderValidator()).thenReturn(mock(TableHeaderValidator.class)); when(validatorProvider.getFieldValidator()).thenReturn(mock(GtfsFieldValidator.class)); diff --git a/core/src/test/java/org/mobilitydata/gtfsvalidator/table/GtfsFeedContainerTest.java b/core/src/test/java/org/mobilitydata/gtfsvalidator/table/GtfsFeedContainerTest.java index 21735bac2b..15073b5d4f 100644 --- a/core/src/test/java/org/mobilitydata/gtfsvalidator/table/GtfsFeedContainerTest.java +++ b/core/src/test/java/org/mobilitydata/gtfsvalidator/table/GtfsFeedContainerTest.java @@ -20,7 +20,6 @@ import com.google.common.collect.ImmutableList; import org.junit.Test; -import org.mobilitydata.gtfsvalidator.table.GtfsTableContainer.TableStatus; import org.mobilitydata.gtfsvalidator.testgtfs.GtfsTestTableContainer; public class GtfsFeedContainerTest { diff --git a/core/src/test/java/org/mobilitydata/gtfsvalidator/testgtfs/GtfsTestTableContainer.java b/core/src/test/java/org/mobilitydata/gtfsvalidator/testgtfs/GtfsTestTableContainer.java index e84ab8041a..94940cced1 100644 --- a/core/src/test/java/org/mobilitydata/gtfsvalidator/testgtfs/GtfsTestTableContainer.java +++ b/core/src/test/java/org/mobilitydata/gtfsvalidator/testgtfs/GtfsTestTableContainer.java @@ -23,8 +23,10 @@ import org.mobilitydata.gtfsvalidator.notice.NoticeContainer; import org.mobilitydata.gtfsvalidator.parsing.CsvHeader; import org.mobilitydata.gtfsvalidator.table.GtfsTableContainer; +import org.mobilitydata.gtfsvalidator.table.TableStatus; -public class GtfsTestTableContainer extends GtfsTableContainer { +public class GtfsTestTableContainer + extends GtfsTableContainer { private static final ImmutableList KEY_COLUMN_NAMES = ImmutableList.of(GtfsTestEntity.ID_FIELD_NAME); diff --git a/core/src/test/java/org/mobilitydata/gtfsvalidator/testgtfs/GtfsTestTableContainer2.java b/core/src/test/java/org/mobilitydata/gtfsvalidator/testgtfs/GtfsTestTableContainer2.java index 962b5bb6e7..662adc7319 100644 --- a/core/src/test/java/org/mobilitydata/gtfsvalidator/testgtfs/GtfsTestTableContainer2.java +++ b/core/src/test/java/org/mobilitydata/gtfsvalidator/testgtfs/GtfsTestTableContainer2.java @@ -23,9 +23,11 @@ import org.mobilitydata.gtfsvalidator.notice.NoticeContainer; import org.mobilitydata.gtfsvalidator.parsing.CsvHeader; import org.mobilitydata.gtfsvalidator.table.GtfsTableContainer; +import org.mobilitydata.gtfsvalidator.table.TableStatus; // We need a second test table class to test multi file validators. -public class GtfsTestTableContainer2 extends GtfsTableContainer { +public class GtfsTestTableContainer2 + extends GtfsTableContainer { private static final ImmutableList KEY_COLUMN_NAMES = ImmutableList.of(GtfsTestEntity.ID_FIELD_NAME); diff --git a/core/src/test/java/org/mobilitydata/gtfsvalidator/testgtfs/GtfsTestTableDescriptor.java b/core/src/test/java/org/mobilitydata/gtfsvalidator/testgtfs/GtfsTestTableDescriptor.java index 58dcdae890..2da7a405a9 100644 --- a/core/src/test/java/org/mobilitydata/gtfsvalidator/testgtfs/GtfsTestTableDescriptor.java +++ b/core/src/test/java/org/mobilitydata/gtfsvalidator/testgtfs/GtfsTestTableDescriptor.java @@ -13,8 +13,7 @@ public class GtfsTestTableDescriptor extends GtfsTableDescriptor { @Override - public GtfsTableContainer createContainerForInvalidStatus( - GtfsTableContainer.TableStatus tableStatus) { + public GtfsTableContainer createContainerForInvalidStatus(TableStatus tableStatus) { return new GtfsTestTableContainer(tableStatus); } diff --git a/core/src/test/java/org/mobilitydata/gtfsvalidator/testgtfs/GtfsTestTableDescriptor2.java b/core/src/test/java/org/mobilitydata/gtfsvalidator/testgtfs/GtfsTestTableDescriptor2.java index c8442428a5..a3896d7237 100644 --- a/core/src/test/java/org/mobilitydata/gtfsvalidator/testgtfs/GtfsTestTableDescriptor2.java +++ b/core/src/test/java/org/mobilitydata/gtfsvalidator/testgtfs/GtfsTestTableDescriptor2.java @@ -14,8 +14,7 @@ // We need a second test table descriptor to test multi file contaioners public class GtfsTestTableDescriptor2 extends GtfsTableDescriptor { @Override - public GtfsTableContainer createContainerForInvalidStatus( - GtfsTableContainer.TableStatus tableStatus) { + public GtfsTableContainer createContainerForInvalidStatus(TableStatus tableStatus) { return new GtfsTestTableContainer2(tableStatus); } diff --git a/core/src/test/java/org/mobilitydata/gtfsvalidator/validator/DefaultValidatorProviderTest.java b/core/src/test/java/org/mobilitydata/gtfsvalidator/validator/DefaultValidatorProviderTest.java index 90d10fa10f..823234582e 100644 --- a/core/src/test/java/org/mobilitydata/gtfsvalidator/validator/DefaultValidatorProviderTest.java +++ b/core/src/test/java/org/mobilitydata/gtfsvalidator/validator/DefaultValidatorProviderTest.java @@ -11,7 +11,7 @@ import org.junit.runners.JUnit4; import org.mobilitydata.gtfsvalidator.TestUtils; import org.mobilitydata.gtfsvalidator.table.GtfsFeedContainer; -import org.mobilitydata.gtfsvalidator.table.GtfsTableContainer.TableStatus; +import org.mobilitydata.gtfsvalidator.table.TableStatus; import org.mobilitydata.gtfsvalidator.testgtfs.GtfsTestEntity; import org.mobilitydata.gtfsvalidator.testgtfs.GtfsTestEntityValidator; import org.mobilitydata.gtfsvalidator.testgtfs.GtfsTestMultiFileValidator; 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 c169d29ac3..eadc3983d0 100644 --- a/core/src/test/java/org/mobilitydata/gtfsvalidator/validator/ValidatorLoaderTest.java +++ b/core/src/test/java/org/mobilitydata/gtfsvalidator/validator/ValidatorLoaderTest.java @@ -24,7 +24,7 @@ import org.mobilitydata.gtfsvalidator.input.CountryCode; import org.mobilitydata.gtfsvalidator.input.DateForValidation; import org.mobilitydata.gtfsvalidator.table.GtfsFeedContainer; -import org.mobilitydata.gtfsvalidator.table.GtfsTableContainer.TableStatus; +import org.mobilitydata.gtfsvalidator.table.TableStatus; import org.mobilitydata.gtfsvalidator.testgtfs.GtfsTestEntityValidator; import org.mobilitydata.gtfsvalidator.testgtfs.GtfsTestSingleFileValidator; import org.mobilitydata.gtfsvalidator.testgtfs.GtfsTestTableContainer; diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/report/model/FeedMetadata.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/report/model/FeedMetadata.java index 832daea602..9bd51bf732 100644 --- a/main/src/main/java/org/mobilitydata/gtfsvalidator/report/model/FeedMetadata.java +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/report/model/FeedMetadata.java @@ -77,13 +77,15 @@ public static FeedMetadata from(GtfsFeedContainer feedContainer, ImmutableSet) - feedContainer.getTableForFilename(GtfsFeedInfo.FILENAME).get()); + Optional> + feedInfoTableOptional = feedContainer.getTableForFilename(GtfsFeedInfo.FILENAME); + feedMetadata.loadFeedInfo(feedInfoTableOptional.get()); + } + if (feedContainer.getTableForFilename(GtfsAgency.FILENAME).isPresent()) { + Optional> agencyTableOptional = + feedContainer.getTableForFilename(GtfsAgency.FILENAME); + feedMetadata.loadAgencyData(agencyTableOptional.get()); } - feedMetadata.loadAgencyData( - (GtfsTableContainer) - feedContainer.getTableForFilename(GtfsAgency.FILENAME).get()); feedMetadata.loadSpecFeatures(feedContainer); return feedMetadata; } @@ -102,7 +104,7 @@ private void setCounts(GtfsFeedContainer feedContainer) { setCount(COUNTS_BLOCKS, feedContainer, GtfsTrip.FILENAME, GtfsTrip.class, GtfsTrip::blockId); } - private , E extends GtfsEntity> void setCount( + private void setCount( String countName, GtfsFeedContainer feedContainer, String fileName, @@ -112,13 +114,11 @@ private , E extends GtfsEntity> void setCount( var table = feedContainer.getTableForFilename(fileName); this.counts.put( countName, - table - .map(gtfsTableContainer -> loadUniqueCount(gtfsTableContainer, clazz, idExtractor)) - .orElse(0)); + table.map(GtfsContainer -> loadUniqueCount(GtfsContainer, clazz, idExtractor)).orElse(0)); } private int loadUniqueCount( - GtfsTableContainer table, Class clazz, Function idExtractor) { + GtfsContainer table, Class clazz, Function idExtractor) { // Iterate through entities and count unique IDs Set uniqueIds = new HashSet<>(); for (GtfsEntity entity : table.getEntities()) { @@ -296,13 +296,14 @@ private void loadRouteColorsFeature(GtfsFeedContainer feedContainer) { List.of((Function) GtfsRoute::hasRouteTextColor))); } - private void loadAgencyData(GtfsTableContainer agencyTable) { + private void loadAgencyData(GtfsContainer agencyTable) { for (GtfsAgency agency : agencyTable.getEntities()) { agencies.add(AgencyMetadata.from(agency)); } } - private void loadFeedInfo(GtfsTableContainer feedTable) { + private void loadFeedInfo( + GtfsTableContainer feedTable) { var info = feedTable.getEntities().isEmpty() ? null : feedTable.getEntities().get(0); feedInfo.put(FEED_INFO_PUBLISHER_NAME, info == null ? "N/A" : info.feedPublisherName()); @@ -347,7 +348,7 @@ private boolean hasAtLeastOneRecordForFields( public ArrayList foundFiles() { var foundFiles = new ArrayList(); for (var table : tableMetaData.values()) { - if (table.getTableStatus() != GtfsTableContainer.TableStatus.MISSING_FILE) { + if (table.getTableStatus() != TableStatus.MISSING_FILE) { foundFiles.add(table.getFilename()); } } diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/report/model/TableMetadata.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/report/model/TableMetadata.java index 5cddcccb5f..e1c1dc7ba4 100644 --- a/main/src/main/java/org/mobilitydata/gtfsvalidator/report/model/TableMetadata.java +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/report/model/TableMetadata.java @@ -1,21 +1,21 @@ package org.mobilitydata.gtfsvalidator.report.model; -import org.mobilitydata.gtfsvalidator.table.GtfsTableContainer; +import org.mobilitydata.gtfsvalidator.table.GtfsContainer; +import org.mobilitydata.gtfsvalidator.table.TableStatus; public class TableMetadata { private final String filename; - private final GtfsTableContainer.TableStatus tableStatus; + private final TableStatus tableStatus; private final int entityCount; - public TableMetadata( - String filename, GtfsTableContainer.TableStatus tableStatus, int entityCount) { + public TableMetadata(String filename, TableStatus tableStatus, int entityCount) { this.filename = filename; this.tableStatus = tableStatus; this.entityCount = entityCount; } - public static TableMetadata from(GtfsTableContainer table) { + public static TableMetadata from(GtfsContainer table) { return new TableMetadata(table.gtfsFilename(), table.getTableStatus(), table.entityCount()); } @@ -23,7 +23,7 @@ public String getFilename() { return filename; } - public GtfsTableContainer.TableStatus getTableStatus() { + public TableStatus getTableStatus() { return tableStatus; } diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsLocationsGeoJsonTypeEnum.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsLocationsGeoJsonTypeEnum.java new file mode 100644 index 0000000000..5d3074c159 --- /dev/null +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsLocationsGeoJsonTypeEnum.java @@ -0,0 +1,22 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mobilitydata.gtfsvalidator.table; + +import org.mobilitydata.gtfsvalidator.annotation.GtfsEnumValue; + +@GtfsEnumValue(name = "FeatureCollection", value = 0) +public interface GtfsLocationsGeoJsonTypeEnum {} diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsLocationsSchema.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsLocationsSchema.java new file mode 100644 index 0000000000..6e2fb9f243 --- /dev/null +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsLocationsSchema.java @@ -0,0 +1,107 @@ +package org.mobilitydata.gtfsvalidator.table; + +import java.util.List; +import org.mobilitydata.gtfsvalidator.annotation.GtfsJson; +import org.mobilitydata.gtfsvalidator.annotation.GtfsJsonEntity; + +enum GeometryType { + POLYGON, + MULTIPOLYGON +} + +abstract class BaseGeometry { + protected GeometryType type; + + public BaseGeometry(GeometryType type) { + this.type = type; + } + + public GeometryType getType() { + return type; + } + + public void setType(GeometryType type) { + this.type = type; + } +} + +class Polygon extends BaseGeometry { + private List>> coordinates; // A list of rings, each a list of coordinate pairs + + public Polygon(List>> coordinates) { + super(GeometryType.POLYGON); + this.coordinates = coordinates; + } + + public List>> getCoordinates() { + return coordinates; + } + + public void setCoordinates(List>> coordinates) { + this.coordinates = coordinates; + } +} + +class MultiPolygon extends BaseGeometry { + private List>>> coordinates; // A list of polygons, each a list of rings + + public MultiPolygon(List>>> coordinates) { + super(GeometryType.MULTIPOLYGON); + this.coordinates = coordinates; + } + + public List>>> getCoordinates() { + return coordinates; + } + + public void setCoordinates(List>>> coordinates) { + this.coordinates = coordinates; + } +} + +class Feature { + private String id; + private BaseGeometry geometry; + private Properties properties; + + public Feature(String id, BaseGeometry geometry, Properties properties) { + this.id = id; + this.geometry = geometry; + this.properties = properties; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public BaseGeometry getGeometry() { + return geometry; + } + + public void setGeometry(BaseGeometry geometry) { + this.geometry = geometry; + } + + public Properties getProperties() { + return properties; + } + + public void setProperties(Properties properties) { + this.properties = properties; + } +} + +class Properties {} + +@GtfsJson("locations.geojson") +public interface GtfsLocationsSchema extends GtfsEntity { + // @FieldType(GtfsLocationsGeoJsonTypeEnum.ID) + String type(); + + @GtfsJsonEntity(value = "features", clazz = Feature.class) + List features(); +} diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/table/locations/GtfsLocations.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/table/locations/GtfsLocations.java new file mode 100644 index 0000000000..6c06e544e6 --- /dev/null +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/table/locations/GtfsLocations.java @@ -0,0 +1,10 @@ +package org.mobilitydata.gtfsvalidator.table.locations; + +import org.mobilitydata.gtfsvalidator.table.GtfsEntity; + +public class GtfsLocations implements GtfsEntity { + @Override + public int csvRowNumber() { + return 0; + } +} diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/table/locations/GtfsLocationsDescriptor.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/table/locations/GtfsLocationsDescriptor.java new file mode 100644 index 0000000000..17e2f57cca --- /dev/null +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/table/locations/GtfsLocationsDescriptor.java @@ -0,0 +1,47 @@ +package org.mobilitydata.gtfsvalidator.table.locations; + +import java.util.List; +import org.mobilitydata.gtfsvalidator.notice.NoticeContainer; +import org.mobilitydata.gtfsvalidator.table.*; + +public class GtfsLocationsDescriptor extends GtfsJsonDescriptor { + + @Override + public GtfsJsonContainer createContainerForEntities( + List entities, NoticeContainer noticeContainer) { + return GtfsLocationsDescriptor.forEntities(this, entities, noticeContainer); + } + + /** Creates a table with entities */ + public static GtfsLocationsJsonContainer forEntities( + GtfsLocationsDescriptor descriptor, + List entities, + NoticeContainer noticeContainer) { + // TODO review indices with the notice container + return new GtfsLocationsJsonContainer(descriptor, entities); + } + + public GtfsLocationsDescriptor() { + super(); + } + + @Override + public GtfsJsonContainer createContainerForInvalidStatus(TableStatus tableStatus) { + return new GtfsLocationsJsonContainer(this, tableStatus); + } + + @Override + public boolean isRecommended() { + return false; + } + + @Override + public Class getEntityClass() { + return null; + } + + @Override + public String gtfsFilename() { + return "locations.geojson"; + } +} diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/table/locations/GtfsLocationsJsonContainer.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/table/locations/GtfsLocationsJsonContainer.java new file mode 100644 index 0000000000..04255eeb48 --- /dev/null +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/table/locations/GtfsLocationsJsonContainer.java @@ -0,0 +1,44 @@ +package org.mobilitydata.gtfsvalidator.table.locations; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.mobilitydata.gtfsvalidator.table.GtfsJsonContainer; +import org.mobilitydata.gtfsvalidator.table.TableStatus; + +public class GtfsLocationsJsonContainer + extends GtfsJsonContainer { + + private final List entities; + + public GtfsLocationsJsonContainer( + GtfsLocationsDescriptor descriptor, List entities) { + super(descriptor, TableStatus.PARSABLE_HEADERS_AND_ROWS); + this.entities = entities; + } + + public GtfsLocationsJsonContainer(GtfsLocationsDescriptor descriptor, TableStatus tableStatus) { + super(descriptor, tableStatus); + this.entities = new ArrayList<>(); + } + + @Override + public Class getEntityClass() { + return null; + } + + @Override + public List getEntities() { + return List.of(); + } + + @Override + public String gtfsFilename() { + return ""; + } + + @Override + public Optional byTranslationKey(String recordId, String recordSubId) { + return Optional.empty(); + } +} diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/TranslationFieldAndReferenceValidator.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/TranslationFieldAndReferenceValidator.java index 45dcc2aabd..98ec78baa2 100644 --- a/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/TranslationFieldAndReferenceValidator.java +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/TranslationFieldAndReferenceValidator.java @@ -27,11 +27,7 @@ import org.mobilitydata.gtfsvalidator.notice.MissingRequiredFieldNotice; import org.mobilitydata.gtfsvalidator.notice.NoticeContainer; import org.mobilitydata.gtfsvalidator.notice.ValidationNotice; -import org.mobilitydata.gtfsvalidator.table.GtfsFeedContainer; -import org.mobilitydata.gtfsvalidator.table.GtfsTableContainer; -import org.mobilitydata.gtfsvalidator.table.GtfsTranslation; -import org.mobilitydata.gtfsvalidator.table.GtfsTranslationSchema; -import org.mobilitydata.gtfsvalidator.table.GtfsTranslationTableContainer; +import org.mobilitydata.gtfsvalidator.table.*; /** * Validates that translations are provided in accordance with GTFS Specification. @@ -125,12 +121,17 @@ private void validateTranslation(GtfsTranslation translation, NoticeContainer no translation, GtfsTranslation.RECORD_SUB_ID_FIELD_NAME, translation.recordSubId())); } } - Optional> parentTable = + Optional> parentTable = feedContainer.getTableForFilename(translation.tableName() + ".txt"); if (parentTable.isEmpty() || parentTable.get().isMissingFile()) { noticeContainer.addValidationNotice(new TranslationUnknownTableNameNotice(translation)); } else if (!translation.hasFieldValue()) { - validateReferenceIntegrity(translation, parentTable.get(), noticeContainer); + if (parentTable.isPresent() && parentTable.get() instanceof GtfsTableContainer) { + validateReferenceIntegrity( + translation, (GtfsTableContainer) parentTable.get(), noticeContainer); + } else { + // TODO check for JSON Tables here + } } } @@ -140,7 +141,7 @@ private void validateTranslation(GtfsTranslation translation, NoticeContainer no */ private void validateReferenceIntegrity( GtfsTranslation translation, - GtfsTableContainer parentTable, + GtfsTableContainer parentTable, NoticeContainer noticeContainer) { ImmutableList keyColumnNames = parentTable.getKeyColumnNames(); if (isMissingOrUnexpectedField( diff --git a/main/src/test/java/org/mobilitydata/gtfsvalidator/report/model/FeedMetadataTest.java b/main/src/test/java/org/mobilitydata/gtfsvalidator/report/model/FeedMetadataTest.java index cdebe4c4f6..45632b602f 100644 --- a/main/src/test/java/org/mobilitydata/gtfsvalidator/report/model/FeedMetadataTest.java +++ b/main/src/test/java/org/mobilitydata/gtfsvalidator/report/model/FeedMetadataTest.java @@ -53,7 +53,7 @@ public void setup() throws IOException, ValidatorLoaderException { private void validateSpecFeature( String specFeature, Boolean expectedValue, - ImmutableList>> tableDescriptors) + ImmutableList>> tableDescriptors) throws IOException, InterruptedException { NoticeContainer noticeContainer = new NoticeContainer(); feedLoaderMock = new GtfsFeedLoader(tableDescriptors); diff --git a/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/DateTripsValidatorTest.java b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/DateTripsValidatorTest.java index 1a8e367351..3b42d555d6 100644 --- a/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/DateTripsValidatorTest.java +++ b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/DateTripsValidatorTest.java @@ -16,7 +16,7 @@ import org.mobilitydata.gtfsvalidator.notice.NoticeContainer; import org.mobilitydata.gtfsvalidator.notice.ValidationNotice; import org.mobilitydata.gtfsvalidator.table.*; -import org.mobilitydata.gtfsvalidator.table.GtfsTableContainer.TableStatus; +import org.mobilitydata.gtfsvalidator.table.TableStatus; import org.mobilitydata.gtfsvalidator.type.GtfsDate; import org.mobilitydata.gtfsvalidator.util.CalendarUtilTest; diff --git a/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/ExpiredCalendarValidatorTest.java b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/ExpiredCalendarValidatorTest.java index eac18ce095..2ad7a1fe0c 100644 --- a/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/ExpiredCalendarValidatorTest.java +++ b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/ExpiredCalendarValidatorTest.java @@ -30,7 +30,7 @@ import org.mobilitydata.gtfsvalidator.input.DateForValidation; import org.mobilitydata.gtfsvalidator.notice.NoticeContainer; import org.mobilitydata.gtfsvalidator.table.*; -import org.mobilitydata.gtfsvalidator.table.GtfsTableContainer.TableStatus; +import org.mobilitydata.gtfsvalidator.table.TableStatus; import org.mobilitydata.gtfsvalidator.type.GtfsDate; @RunWith(JUnit4.class) diff --git a/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/MatchingFeedAndAgencyLangValidatorTest.java b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/MatchingFeedAndAgencyLangValidatorTest.java index bfeef3dc7c..6bdd7750c9 100644 --- a/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/MatchingFeedAndAgencyLangValidatorTest.java +++ b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/MatchingFeedAndAgencyLangValidatorTest.java @@ -31,7 +31,7 @@ import org.mobilitydata.gtfsvalidator.table.GtfsAgencyTableContainer; import org.mobilitydata.gtfsvalidator.table.GtfsFeedInfo; import org.mobilitydata.gtfsvalidator.table.GtfsFeedInfoTableContainer; -import org.mobilitydata.gtfsvalidator.table.GtfsTableContainer.TableStatus; +import org.mobilitydata.gtfsvalidator.table.TableStatus; import org.mobilitydata.gtfsvalidator.validator.MatchingFeedAndAgencyLangValidator.FeedInfoLangAndAgencyLangMismatchNotice; @RunWith(JUnit4.class) diff --git a/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/MissingCalendarAndCalendarDateValidatorTest.java b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/MissingCalendarAndCalendarDateValidatorTest.java index afb6e585c1..f9621bb995 100644 --- a/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/MissingCalendarAndCalendarDateValidatorTest.java +++ b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/MissingCalendarAndCalendarDateValidatorTest.java @@ -31,7 +31,7 @@ import org.mobilitydata.gtfsvalidator.table.GtfsCalendarDate; import org.mobilitydata.gtfsvalidator.table.GtfsCalendarDateTableContainer; import org.mobilitydata.gtfsvalidator.table.GtfsCalendarTableContainer; -import org.mobilitydata.gtfsvalidator.table.GtfsTableContainer.TableStatus; +import org.mobilitydata.gtfsvalidator.table.TableStatus; import org.mobilitydata.gtfsvalidator.type.GtfsDate; import org.mobilitydata.gtfsvalidator.util.CalendarUtilTest; import org.mobilitydata.gtfsvalidator.validator.MissingCalendarAndCalendarDateValidator.MissingCalendarAndCalendarDateFilesNotice; diff --git a/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/MissingFeedInfoValidatorTest.java b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/MissingFeedInfoValidatorTest.java index 0dd0553dff..43b109f93a 100644 --- a/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/MissingFeedInfoValidatorTest.java +++ b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/MissingFeedInfoValidatorTest.java @@ -25,9 +25,8 @@ private static List generateNotices( public void missingFeedInfoTranslationTableNotPresent() { assertThat( generateNotices( - GtfsFeedInfoTableContainer.forStatus(GtfsTableContainer.TableStatus.MISSING_FILE), - GtfsTranslationTableContainer.forStatus( - GtfsTableContainer.TableStatus.MISSING_FILE))) + GtfsFeedInfoTableContainer.forStatus(TableStatus.MISSING_FILE), + GtfsTranslationTableContainer.forStatus(TableStatus.MISSING_FILE))) .containsExactly(new MissingRecommendedFileNotice(GtfsFeedInfo.FILENAME)); } @@ -35,9 +34,8 @@ public void missingFeedInfoTranslationTableNotPresent() { public void missingFeedInfoWhenTranslationTableIsPresent() { assertThat( generateNotices( - GtfsFeedInfoTableContainer.forStatus(GtfsTableContainer.TableStatus.MISSING_FILE), - GtfsTranslationTableContainer.forStatus( - GtfsTableContainer.TableStatus.PARSABLE_HEADERS_AND_ROWS))) + GtfsFeedInfoTableContainer.forStatus(TableStatus.MISSING_FILE), + GtfsTranslationTableContainer.forStatus(TableStatus.PARSABLE_HEADERS_AND_ROWS))) .contains(new MissingRequiredFileNotice(GtfsFeedInfo.FILENAME)); } @@ -45,10 +43,8 @@ public void missingFeedInfoWhenTranslationTableIsPresent() { public void feedInfoPresentShouldGenerateNoNotice() { assertThat( generateNotices( - GtfsFeedInfoTableContainer.forStatus( - GtfsTableContainer.TableStatus.PARSABLE_HEADERS_AND_ROWS), - GtfsTranslationTableContainer.forStatus( - GtfsTableContainer.TableStatus.PARSABLE_HEADERS_AND_ROWS))) + GtfsFeedInfoTableContainer.forStatus(TableStatus.PARSABLE_HEADERS_AND_ROWS), + GtfsTranslationTableContainer.forStatus(TableStatus.PARSABLE_HEADERS_AND_ROWS))) .isEmpty(); } } diff --git a/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/NetworkIdConsistencyValidatorTest.java b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/NetworkIdConsistencyValidatorTest.java index 0dfe65a6d2..baf0358156 100644 --- a/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/NetworkIdConsistencyValidatorTest.java +++ b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/NetworkIdConsistencyValidatorTest.java @@ -28,10 +28,9 @@ public void setup() { noticeContainer); routeNetworkTableContainer = new GtfsRouteNetworkTableContainer( - new GtfsRouteNetworkTableDescriptor(), GtfsTableContainer.TableStatus.MISSING_FILE); + new GtfsRouteNetworkTableDescriptor(), TableStatus.MISSING_FILE); networkTableContainer = - new GtfsNetworkTableContainer( - new GtfsNetworkTableDescriptor(), GtfsTableContainer.TableStatus.MISSING_FILE); + new GtfsNetworkTableContainer(new GtfsNetworkTableDescriptor(), TableStatus.MISSING_FILE); } @Test diff --git a/model/src/main/java/org/mobilitydata/gtfsvalidator/annotation/GtfsJson.java b/model/src/main/java/org/mobilitydata/gtfsvalidator/annotation/GtfsJson.java new file mode 100644 index 0000000000..dce0723493 --- /dev/null +++ b/model/src/main/java/org/mobilitydata/gtfsvalidator/annotation/GtfsJson.java @@ -0,0 +1,39 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mobilitydata.gtfsvalidator.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotates an interface that defines schema for a single GTFS JSON, such as "locations.geojson". + * + *

Example. + * + *

+ *   {@literal @}GtfsJson("locations.geojson")
+ *   public interface GtfsLocationsSchema extends GtfsEntity {
+ *   }
+ * 
+ */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface GtfsJson { + String value(); +} diff --git a/model/src/main/java/org/mobilitydata/gtfsvalidator/annotation/GtfsJsonEntity.java b/model/src/main/java/org/mobilitydata/gtfsvalidator/annotation/GtfsJsonEntity.java new file mode 100644 index 0000000000..ff15fcc7f9 --- /dev/null +++ b/model/src/main/java/org/mobilitydata/gtfsvalidator/annotation/GtfsJsonEntity.java @@ -0,0 +1,15 @@ +package org.mobilitydata.gtfsvalidator.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** Annotates a field that defines entities for a GTFS JSON schema. */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface GtfsJsonEntity { + String value(); + + Class clazz(); +} diff --git a/processor/notices/src/main/java/org/mobilitydata/gtfsvalidator/processor/notices/NoticesProcessor.java b/processor/notices/src/main/java/org/mobilitydata/gtfsvalidator/processor/notices/NoticesProcessor.java index 5b489a1822..0211b88787 100644 --- a/processor/notices/src/main/java/org/mobilitydata/gtfsvalidator/processor/notices/NoticesProcessor.java +++ b/processor/notices/src/main/java/org/mobilitydata/gtfsvalidator/processor/notices/NoticesProcessor.java @@ -52,31 +52,35 @@ public SourceVersion getSupportedSourceVersion() { @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { - for (TypeElement element : - typesIn(roundEnv.getElementsAnnotatedWith(GtfsValidationNotice.class))) { - Optional comments = docCommentsFactory.create(element); - if (comments.isEmpty()) { - continue; - } + try { + for (TypeElement element : + typesIn(roundEnv.getElementsAnnotatedWith(GtfsValidationNotice.class))) { + Optional comments = docCommentsFactory.create(element); + if (comments.isEmpty()) { + continue; + } - PackageElement packageElement = processingEnv.getElementUtils().getPackageOf(element); - String resourceName = NoticeDocComments.getResourceNameForTypeElement(element); + PackageElement packageElement = processingEnv.getElementUtils().getPackageOf(element); + String resourceName = NoticeDocComments.getResourceNameForTypeElement(element); - try { - FileObject resource = - processingEnv - .getFiler() - .createResource( - StandardLocation.CLASS_OUTPUT, - packageElement.getQualifiedName(), - resourceName, - element); - try (Writer writer = resource.openWriter()) { - GSON.toJson(comments.get(), writer); + try { + FileObject resource = + processingEnv + .getFiler() + .createResource( + StandardLocation.CLASS_OUTPUT, + packageElement.getQualifiedName(), + resourceName, + element); + try (Writer writer = resource.openWriter()) { + GSON.toJson(comments.get(), writer); + } + } catch (IOException e) { + throw new RuntimeException(e); } - } catch (IOException e) { - throw new RuntimeException(e); } + } catch (Exception e) { + System.out.println("Error: " + e); } return false; } diff --git a/processor/src/main/java/org/mobilitydata/gtfsvalidator/processor/GtfsAnnotationProcessor.java b/processor/src/main/java/org/mobilitydata/gtfsvalidator/processor/GtfsAnnotationProcessor.java index afa907421a..210f5178ca 100644 --- a/processor/src/main/java/org/mobilitydata/gtfsvalidator/processor/GtfsAnnotationProcessor.java +++ b/processor/src/main/java/org/mobilitydata/gtfsvalidator/processor/GtfsAnnotationProcessor.java @@ -37,10 +37,7 @@ import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.tools.Diagnostic; -import org.mobilitydata.gtfsvalidator.annotation.GtfsEnumValue; -import org.mobilitydata.gtfsvalidator.annotation.GtfsEnumValues; -import org.mobilitydata.gtfsvalidator.annotation.GtfsTable; -import org.mobilitydata.gtfsvalidator.annotation.GtfsValidator; +import org.mobilitydata.gtfsvalidator.annotation.*; /** * Processor that generates data classes, loaders and validators based on annotations on GTFS schema @@ -51,6 +48,9 @@ public class GtfsAnnotationProcessor extends AbstractProcessor { private final Analyser analyser = new Analyser(); + public GtfsAnnotationProcessor() { + super(); + } /** * Sanitizes the result of {@link RoundEnvironment#getElementsAnnotatedWith}, which otherwise can * contain elements annotated with annotations of ERROR type. diff --git a/processor/src/main/java/org/mobilitydata/gtfsvalidator/processor/TableContainerGenerator.java b/processor/src/main/java/org/mobilitydata/gtfsvalidator/processor/TableContainerGenerator.java index f215ecdf0a..2dc5735698 100644 --- a/processor/src/main/java/org/mobilitydata/gtfsvalidator/processor/TableContainerGenerator.java +++ b/processor/src/main/java/org/mobilitydata/gtfsvalidator/processor/TableContainerGenerator.java @@ -32,6 +32,7 @@ import org.mobilitydata.gtfsvalidator.parsing.CsvHeader; import org.mobilitydata.gtfsvalidator.table.GtfsTableContainer; import org.mobilitydata.gtfsvalidator.table.GtfsTableDescriptor; +import org.mobilitydata.gtfsvalidator.table.TableStatus; /** * Generates code for a container for a loaded GTFS table. @@ -64,7 +65,12 @@ public TypeSpec generateGtfsContainerClass() { TypeSpec.Builder typeSpec = TypeSpec.classBuilder(classNames.tableContainerSimpleName()) .superclass( - ParameterizedTypeName.get(ClassName.get(GtfsTableContainer.class), gtfsEntityType)) + ParameterizedTypeName.get( + ClassName.get(GtfsTableContainer.class), + classNames.entityImplementationTypeName(), + ParameterizedTypeName.get( + ClassName.get(GtfsTableDescriptor.class), + classNames.entityImplementationTypeName()))) .addAnnotation(Generated.class) .addModifiers(Modifier.PUBLIC, Modifier.FINAL); @@ -126,7 +132,7 @@ private MethodSpec generateConstructorWithStatus() { return MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) .addParameter(tableDescriptorType, "descriptor") - .addParameter(GtfsTableContainer.TableStatus.class, "tableStatus") + .addParameter(TableStatus.class, "tableStatus") .addStatement("super(descriptor, tableStatus, $T.EMPTY)", CsvHeader.class) .addStatement("this.entities = new $T<>()", ArrayList.class) .build(); @@ -182,7 +188,7 @@ private MethodSpec generateForStatusMethod() { "Creates a table with the given TableStatus. This method is intended to be" + " used in tests.") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - .addParameter(GtfsTableContainer.TableStatus.class, "tableStatus") + .addParameter(TableStatus.class, "tableStatus") .addStatement( "return new $T(new $T(), tableStatus)", tableContainerTypeName, diff --git a/processor/src/main/java/org/mobilitydata/gtfsvalidator/processor/TableDescriptorGenerator.java b/processor/src/main/java/org/mobilitydata/gtfsvalidator/processor/TableDescriptorGenerator.java index 514bbe98c9..187f7fbc71 100644 --- a/processor/src/main/java/org/mobilitydata/gtfsvalidator/processor/TableDescriptorGenerator.java +++ b/processor/src/main/java/org/mobilitydata/gtfsvalidator/processor/TableDescriptorGenerator.java @@ -42,11 +42,7 @@ import org.mobilitydata.gtfsvalidator.parsing.CsvHeader; import org.mobilitydata.gtfsvalidator.parsing.FieldCache; import org.mobilitydata.gtfsvalidator.parsing.RowParser; -import org.mobilitydata.gtfsvalidator.table.GtfsColumnDescriptor; -import org.mobilitydata.gtfsvalidator.table.GtfsEntityBuilder; -import org.mobilitydata.gtfsvalidator.table.GtfsFieldLoader; -import org.mobilitydata.gtfsvalidator.table.GtfsTableContainer; -import org.mobilitydata.gtfsvalidator.table.GtfsTableDescriptor; +import org.mobilitydata.gtfsvalidator.table.*; /** * Generates code for a GtfsTableDescriptor subclass for a specific GTFS table. @@ -149,7 +145,7 @@ private MethodSpec generateCreateContainerForInvalidStatusMethod() { return MethodSpec.methodBuilder("createContainerForInvalidStatus") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC) - .addParameter(GtfsTableContainer.TableStatus.class, "tableStatus") + .addParameter(TableStatus.class, "tableStatus") .returns(GtfsTableContainer.class) .addStatement("return new $T(this, tableStatus)", classNames.tableContainerTypeName()) .build(); diff --git a/processor/tests/src/test/java/org/mobilitydata/gtfsvalidator/processor/tests/CurrencyAmountSchemaTest.java b/processor/tests/src/test/java/org/mobilitydata/gtfsvalidator/processor/tests/CurrencyAmountSchemaTest.java deleted file mode 100644 index a2eafc802f..0000000000 --- a/processor/tests/src/test/java/org/mobilitydata/gtfsvalidator/processor/tests/CurrencyAmountSchemaTest.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2022 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.mobilitydata.gtfsvalidator.processor.tests; - -import static com.google.common.truth.Truth.assertThat; - -import com.google.common.collect.ImmutableList; -import java.math.BigDecimal; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -import org.mobilitydata.gtfsvalidator.notice.InvalidCurrencyAmountNotice; -import org.mobilitydata.gtfsvalidator.table.CurrencyAmountTableDescriptor; -import org.mobilitydata.gtfsvalidator.testing.LoadingHelper; -import org.mobilitydata.gtfsvalidator.validator.CurrencyAmountCurrencyAmountValidator; -import org.mobilitydata.gtfsvalidator.validator.ValidatorLoader; -import org.mobilitydata.gtfsvalidator.validator.ValidatorLoaderException; - -@RunWith(JUnit4.class) -public class CurrencyAmountSchemaTest { - - private CurrencyAmountTableDescriptor tableDescriptor; - private LoadingHelper helper; - - @Before - public void setup() throws ValidatorLoaderException { - tableDescriptor = new CurrencyAmountTableDescriptor(); - helper = new LoadingHelper(); - helper.setValidatorLoader( - ValidatorLoader.createForClasses( - ImmutableList.of(CurrencyAmountCurrencyAmountValidator.class))); - } - - @Test - public void testValidCurrencyUSD() throws ValidatorLoaderException { - - helper.load(tableDescriptor, "amount,currency", "1.50,USD"); - - assertThat(helper.getValidationNotices()).isEmpty(); - } - - @Test - public void testInvalidCurrencyUSD() throws ValidatorLoaderException { - helper.load(tableDescriptor, "amount,currency", "1.5,USD"); - - assertThat(helper.getValidationNotices()) - .containsExactly( - new InvalidCurrencyAmountNotice( - "currency_amount.txt", "amount", 2, new BigDecimal("1.5"))); - } - - @Test - public void testValidCurrencyISK() throws ValidatorLoaderException { - // Icelandic króna expects no digits after decimal separator. - helper.load(tableDescriptor, "amount,currency", "5,ISK"); - - assertThat(helper.getValidationNotices()).isEmpty(); - } - - @Test - public void testInvalidCurrencyISK() throws ValidatorLoaderException { - // Icelandic króna expects no digits after decimal separator. - helper.load(tableDescriptor, "amount,currency", "5.0,ISK"); - - assertThat(helper.getValidationNotices()) - .containsExactly( - new InvalidCurrencyAmountNotice( - "currency_amount.txt", "amount", 2, new BigDecimal("5.0"))); - } -} diff --git a/processor/tests/src/test/java/org/mobilitydata/gtfsvalidator/processor/tests/MixedCaseSchemaTest.java b/processor/tests/src/test/java/org/mobilitydata/gtfsvalidator/processor/tests/MixedCaseSchemaTest.java deleted file mode 100644 index 46a4b9a1ca..0000000000 --- a/processor/tests/src/test/java/org/mobilitydata/gtfsvalidator/processor/tests/MixedCaseSchemaTest.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2022 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.mobilitydata.gtfsvalidator.processor.tests; - -import static com.google.common.truth.Truth.assertThat; - -import com.google.common.collect.ImmutableList; -import java.util.Arrays; -import java.util.Collection; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.mobilitydata.gtfsvalidator.notice.MixedCaseRecommendedFieldNotice; -import org.mobilitydata.gtfsvalidator.table.MixedCaseTest; -import org.mobilitydata.gtfsvalidator.table.MixedCaseTestTableDescriptor; -import org.mobilitydata.gtfsvalidator.testing.LoadingHelper; -import org.mobilitydata.gtfsvalidator.validator.MixedCaseTestMixedCaseValidator; -import org.mobilitydata.gtfsvalidator.validator.ValidatorLoader; -import org.mobilitydata.gtfsvalidator.validator.ValidatorLoaderException; - -@RunWith(Parameterized.class) -public class MixedCaseSchemaTest { - private final String value; - private final boolean isValid; - - public MixedCaseSchemaTest(String value, boolean isValid) { - this.value = value; - this.isValid = isValid; - } - - private MixedCaseTestTableDescriptor tableDescriptor; - private LoadingHelper helper; - - @Before - public void setup() throws ValidatorLoaderException { - tableDescriptor = new MixedCaseTestTableDescriptor(); - helper = new LoadingHelper(); - helper.setValidatorLoader( - ValidatorLoader.createForClasses(ImmutableList.of(MixedCaseTestMixedCaseValidator.class))); - } - - @Parameterized.Parameters - public static Collection data() { - return Arrays.asList( - new Object[][] { - // valid values - {"Mixed-Case", true}, - {"Mixed_Case", true}, - {"Mixed Case", true}, - {"22222", true}, - {"A1", true}, - {"ZA112", true}, - {"301", true}, - {"RTE 30", true}, - {"급 행 12", true}, - {"급행12", true}, - {"東西線", true}, - {"101B", true}, - {"A14C", true}, - {"A14c", true}, - {"A14-C", true}, - {"A14_C", true}, - {"A14 C", true}, - {"Route 1", true}, - {"Route 1 Boulevard", true}, - {"ZA12", true}, - {"Avenue des Champs-Élysées", true}, - {"UPPERCASE", true}, - {"ROUTE 22", true}, - {"ROUTE 1", true}, - {"route 1 Boulevard", true}, - {"Sentences are ok with one mixed case word", true}, - {"MixedCaseButSingleWord", true}, - // invalid values - {"lowercase", false}, - {"snake_case", false}, - {"kebab-case", false}, - {"UPPER-CASE", false}, - {"lower case space", false}, - {"34broadst", false}, - }); - } - - @Test - public void testMixedCase() throws ValidatorLoaderException { - helper.load(tableDescriptor, MixedCaseTest.SOME_FIELD_FIELD_NAME, value); - if (isValid) { - assertThat(helper.getValidationNotices()).isEmpty(); - } else { - assertThat(helper.getValidationNotices()) - .containsExactly( - new MixedCaseRecommendedFieldNotice( - MixedCaseTest.FILENAME, MixedCaseTest.SOME_FIELD_FIELD_NAME, value, 2)); - } - } -} diff --git a/web/client/.gitignore b/web/client/.gitignore index fef3daace4..d907181126 100644 --- a/web/client/.gitignore +++ b/web/client/.gitignore @@ -12,3 +12,4 @@ vite.config.ts.timestamp-* rules.json cypress/screenshots/ cypress/videos/ +/static/RULES.md