Skip to content

Commit

Permalink
feat: recommended annotation (#1149)
Browse files Browse the repository at this point in the history
* feat: recommended file annotation

* feat/recommended-field-annotation
  • Loading branch information
KClough authored Jun 16, 2022
1 parent e00672c commit 41f8211
Show file tree
Hide file tree
Showing 12 changed files with 240 additions and 109 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* 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;

/** The level for fields. */
public enum FieldLevelEnum {
REQUIRED,
RECOMMENDED,
OPTIONAL,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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;

/**
* Adds a validation that the field or a file is recommended.
*
* <p>Example.
*
* <pre>
* {@literal @}Recommended
* {@literal @}GtfsTable(value = "feed_info.txt", singleRow = true) {
* ...
* }
* </pre>
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Recommended {}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.util.Locale;
import java.util.function.Function;
import javax.annotation.Nullable;
import org.mobilitydata.gtfsvalidator.annotation.FieldLevelEnum;
import org.mobilitydata.gtfsvalidator.notice.EmptyRowNotice;
import org.mobilitydata.gtfsvalidator.notice.InvalidColorNotice;
import org.mobilitydata.gtfsvalidator.notice.InvalidCurrencyNotice;
Expand Down Expand Up @@ -52,8 +53,6 @@
*/
public class RowParser {

public static final boolean REQUIRED = true;
public static final boolean OPTIONAL = false;
private final String fileName;
private final CsvHeader header;
private final GtfsFieldValidator fieldValidator;
Expand Down Expand Up @@ -107,9 +106,9 @@ public boolean checkRowLength() {
}

@Nullable
public String asString(int columnIndex, boolean required) {
public String asString(int columnIndex, FieldLevelEnum level) {
String s = row.asString(columnIndex);
if (required && s == null) {
if (level == FieldLevelEnum.REQUIRED && s == null) {
noticeContainer.addValidationNotice(
new MissingRequiredFieldNotice(
fileName, row.getRowNumber(), header.getColumnName(columnIndex)));
Expand All @@ -126,23 +125,23 @@ public String asString(int columnIndex, boolean required) {
}

@Nullable
public String asText(int columnIndex, boolean required) {
return asString(columnIndex, required);
public String asText(int columnIndex, FieldLevelEnum level) {
return asString(columnIndex, level);
}

@Nullable
public String asId(int columnIndex, boolean required) {
return asValidatedString(columnIndex, required, fieldValidator::validateId);
public String asId(int columnIndex, FieldLevelEnum level) {
return asValidatedString(columnIndex, level, fieldValidator::validateId);
}

@Nullable
public String asUrl(int columnIndex, boolean required) {
return asValidatedString(columnIndex, required, fieldValidator::validateUrl);
public String asUrl(int columnIndex, FieldLevelEnum level) {
return asValidatedString(columnIndex, level, fieldValidator::validateUrl);
}

@Nullable
public String asEmail(int columnIndex, boolean required) {
return asValidatedString(columnIndex, required, fieldValidator::validateEmail);
public String asEmail(int columnIndex, FieldLevelEnum level) {
return asValidatedString(columnIndex, level, fieldValidator::validateEmail);
}

/**
Expand All @@ -151,45 +150,44 @@ public String asEmail(int columnIndex, boolean required) {
* unknown, only phone number starting by "+" are validated.
*
* @param columnIndex the column index
* @param required true is the value is required, false otherwise
* @param level whether the value is required, recommended or optional according to GTFS
* @return the string value of the phone number to be validated if a valid number according to the
* {@code CountryCode}, returns {@code null} otherwise. Note that if {@code CountryCode} is
* unknown, only phone number starting by "+" are validated.
*/
@Nullable
public String asPhoneNumber(int columnIndex, boolean required) {
return asValidatedString(columnIndex, required, fieldValidator::validatePhoneNumber);
public String asPhoneNumber(int columnIndex, FieldLevelEnum level) {
return asValidatedString(columnIndex, level, fieldValidator::validatePhoneNumber);
}

@Nullable
public Locale asLanguageCode(int columnIndex, boolean required) {
return parseAsType(
columnIndex, required, Locale::forLanguageTag, InvalidLanguageCodeNotice::new);
public Locale asLanguageCode(int columnIndex, FieldLevelEnum level) {
return parseAsType(columnIndex, level, Locale::forLanguageTag, InvalidLanguageCodeNotice::new);
}

@Nullable
public ZoneId asTimezone(int columnIndex, boolean required) {
return parseAsType(columnIndex, required, ZoneId::of, InvalidTimezoneNotice::new);
public ZoneId asTimezone(int columnIndex, FieldLevelEnum level) {
return parseAsType(columnIndex, level, ZoneId::of, InvalidTimezoneNotice::new);
}

@Nullable
public Currency asCurrencyCode(int columnIndex, boolean required) {
return parseAsType(columnIndex, required, Currency::getInstance, InvalidCurrencyNotice::new);
public Currency asCurrencyCode(int columnIndex, FieldLevelEnum level) {
return parseAsType(columnIndex, level, Currency::getInstance, InvalidCurrencyNotice::new);
}

@Nullable
public Double asFloat(int columnIndex, boolean required) {
return parseAsType(columnIndex, required, Double::parseDouble, InvalidFloatNotice::new);
public Double asFloat(int columnIndex, FieldLevelEnum level) {
return parseAsType(columnIndex, level, Double::parseDouble, InvalidFloatNotice::new);
}

@Nullable
public Double asFloat(int columnIndex, boolean required, NumberBounds bounds) {
return checkBounds(asFloat(columnIndex, required), 0.0, columnIndex, "float", bounds);
public Double asFloat(int columnIndex, FieldLevelEnum level, NumberBounds bounds) {
return checkBounds(asFloat(columnIndex, level), 0.0, columnIndex, "float", bounds);
}

@Nullable
public Double asLatitude(int columnIndex, boolean required) {
Double value = asFloat(columnIndex, required);
public Double asLatitude(int columnIndex, FieldLevelEnum level) {
Double value = asFloat(columnIndex, level);
if (value != null && !(-90 <= value && value <= 90)) {
noticeContainer.addValidationNotice(
new NumberOutOfRangeNotice(
Expand All @@ -204,8 +202,8 @@ public Double asLatitude(int columnIndex, boolean required) {
}

@Nullable
public Double asLongitude(int columnIndex, boolean required) {
Double value = asFloat(columnIndex, required);
public Double asLongitude(int columnIndex, FieldLevelEnum level) {
Double value = asFloat(columnIndex, level);
if (value != null && !(-180 <= value && value <= 180)) {
noticeContainer.addValidationNotice(
new NumberOutOfRangeNotice(
Expand All @@ -220,24 +218,24 @@ public Double asLongitude(int columnIndex, boolean required) {
}

@Nullable
public Integer asInteger(int columnIndex, boolean required) {
return parseAsType(columnIndex, required, Integer::parseInt, InvalidIntegerNotice::new);
public Integer asInteger(int columnIndex, FieldLevelEnum level) {
return parseAsType(columnIndex, level, Integer::parseInt, InvalidIntegerNotice::new);
}

@Nullable
public Integer asInteger(int columnIndex, boolean required, NumberBounds bounds) {
return checkBounds(asInteger(columnIndex, required), 0, columnIndex, "integer", bounds);
public Integer asInteger(int columnIndex, FieldLevelEnum level, NumberBounds bounds) {
return checkBounds(asInteger(columnIndex, level), 0, columnIndex, "integer", bounds);
}

@Nullable
public BigDecimal asDecimal(int columnIndex, boolean required) {
return parseAsType(columnIndex, required, BigDecimal::new, InvalidFloatNotice::new);
public BigDecimal asDecimal(int columnIndex, FieldLevelEnum level) {
return parseAsType(columnIndex, level, BigDecimal::new, InvalidFloatNotice::new);
}

@Nullable
public BigDecimal asDecimal(int columnIndex, boolean required, NumberBounds bounds) {
public BigDecimal asDecimal(int columnIndex, FieldLevelEnum level, NumberBounds bounds) {
return checkBounds(
asDecimal(columnIndex, required), new BigDecimal(0), columnIndex, "decimal", bounds);
asDecimal(columnIndex, level), new BigDecimal(0), columnIndex, "decimal", bounds);
}

/**
Expand Down Expand Up @@ -296,13 +294,13 @@ private <T extends Comparable<T>> T checkBounds(
}

@Nullable
public GtfsColor asColor(int columnIndex, boolean required) {
return parseAsType(columnIndex, required, GtfsColor::fromString, InvalidColorNotice::new);
public GtfsColor asColor(int columnIndex, FieldLevelEnum level) {
return parseAsType(columnIndex, level, GtfsColor::fromString, InvalidColorNotice::new);
}

@Nullable
public <E> Integer asEnum(int columnIndex, boolean required, EnumCreator<E> enumCreator) {
Integer i = asInteger(columnIndex, required);
public <E> Integer asEnum(int columnIndex, FieldLevelEnum level, EnumCreator<E> enumCreator) {
Integer i = asInteger(columnIndex, level);
if (i == null) {
return null;
}
Expand All @@ -315,13 +313,13 @@ public <E> Integer asEnum(int columnIndex, boolean required, EnumCreator<E> enum
}

@Nullable
public GtfsTime asTime(int columnIndex, boolean required) {
return parseAsType(columnIndex, required, GtfsTime::fromString, InvalidTimeNotice::new);
public GtfsTime asTime(int columnIndex, FieldLevelEnum level) {
return parseAsType(columnIndex, level, GtfsTime::fromString, InvalidTimeNotice::new);
}

@Nullable
public GtfsDate asDate(int columnIndex, boolean required) {
return parseAsType(columnIndex, required, GtfsDate::fromString, InvalidDateNotice::new);
public GtfsDate asDate(int columnIndex, FieldLevelEnum level) {
return parseAsType(columnIndex, level, GtfsDate::fromString, InvalidDateNotice::new);
}

public enum NumberBounds {
Expand All @@ -341,7 +339,7 @@ public interface EnumCreator<E> {
* parsing failed.
*
* @param columnIndex index of the column to parse
* @param required whether the value is required according to GTFS
* @param level whether the value is required, recommended or optional according to GTFS
* @param parsingFunction function that converts string to an object to return
* @param noticingFunction function to create a notice about parse errors
* @param <T> the type to return
Expand All @@ -350,10 +348,10 @@ public interface EnumCreator<E> {
@Nullable
private <T> T parseAsType(
int columnIndex,
boolean required,
FieldLevelEnum level,
Function<String, T> parsingFunction,
NoticingFunction noticingFunction) {
String s = asString(columnIndex, required);
String s = asString(columnIndex, level);
if (s == null) {
return null;
}
Expand All @@ -376,14 +374,14 @@ private <T> T parseAsType(
* error is emitted, the value is considered invalid.
*
* @param columnIndex index of the column to parse
* @param required whether the value is required according to GTFS
* @param level whether the value is required, recommended or optional according to GTFS
* @param validatingFunction the predicate to validate a given string
* @return the cell value at the given column or null if the value is missing
*/
@Nullable
private String asValidatedString(
int columnIndex, boolean required, FieldValidatingFunction validatingFunction) {
String s = asString(columnIndex, required);
int columnIndex, FieldLevelEnum level, FieldValidatingFunction validatingFunction) {
String s = asString(columnIndex, level);
if (s == null) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,15 @@ public boolean isMissingFile() {
return tableStatus == TableStatus.MISSING_FILE;
}

/**
* Tells if the file is recommended according to GTFS.
*
* <p>Note that a recommended file may be empty.
*
* @return true if the file is recommended, false otherwise
*/
public abstract boolean isRecommended();

/**
* Tells if the file is required according to GTFS.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
public abstract class GtfsTableLoader<T extends GtfsEntity> {
public abstract String gtfsFilename();

public abstract boolean isRecommended();

public abstract boolean isRequired();

public abstract Set<String> getColumnNames();
Expand Down
Loading

0 comments on commit 41f8211

Please sign in to comment.