Skip to content

Commit

Permalink
enable table validation for tables in MultiTableModel (refs #231)
Browse files Browse the repository at this point in the history
  • Loading branch information
mborne committed Jul 12, 2021
1 parent 52c8fc7 commit e541eae
Show file tree
Hide file tree
Showing 14 changed files with 204 additions and 86 deletions.
6 changes: 0 additions & 6 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,6 @@ language: java
jdk:
- openjdk11

addons:
apt:
packages:
- locales
- language-pack-fr

matrix:
include:
- os: linux
Expand Down
4 changes: 2 additions & 2 deletions travis/build-openjdk11.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ echo "-- Display informations"
echo "-----------------------------------------------------------------------"

# XSD validation errors
export LANG=fr_FR.UTF-8
export LC_ALL=fr_FR.UTF-8
export LANG=en_EN.UTF-8
export LC_ALL=en_EN.UTF-8

export OGR2OGR_PATH=${OGR2OGR_PATH:-ogr2ogr}
echo "-- OGR2OGR_PATH : ${OGR2OGR_PATH}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import fr.ign.validator.string.transform.IsoControlEscaper;
import fr.ign.validator.validation.Validatable;
import fr.ign.validator.validation.Validator;
import fr.ign.validator.validation.attribute.GeometryErrorCode;

/**
* Represents an attribute of a Feature (value associated to a type)
Expand Down Expand Up @@ -93,8 +92,8 @@ public void validate(Context context) {
IsoControlEscaper transform = new IsoControlEscaper(false);
if (type.isGeometry()) {
context.report(
context.createError(CoreErrorCodes.ATTRIBUTE_GEOMETRY_INVALID)
.setMessageParam("TYPE_ERROR", GeometryErrorCode.INVALID_WKT.getMessage())
context.createError(CoreErrorCodes.ATTRIBUTE_GEOMETRY_INVALID_FORMAT)
.setMessageParam("VALUE", value.toString())
);
} else {
context.report(
Expand Down
16 changes: 7 additions & 9 deletions validator-core/src/main/java/fr/ign/validator/data/Header.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package fr.ign.validator.data;

import java.io.File;

import fr.ign.validator.Context;
import fr.ign.validator.error.CoreErrorCodes;
import fr.ign.validator.mapping.FeatureTypeMapper;
Expand All @@ -19,9 +17,9 @@
public class Header implements Validatable {

/**
* TODO rely on Context
* Relative path for the table in order to report errors.
*/
private File matchingFile;
private String relativePath;

/**
* mapping columns/FeatureType
Expand All @@ -32,8 +30,8 @@ public class Header implements Validatable {
* @param columns
* @param mapping
*/
public Header(File matchingFile, FeatureTypeMapper mapping) {
this.matchingFile = matchingFile;
public Header(String relativePath, FeatureTypeMapper mapping) {
this.relativePath = relativePath;
this.mapping = mapping;
}

Expand Down Expand Up @@ -69,19 +67,19 @@ public void validate(Context context) {
if (missingAttribute.getName().equals("WKT")) {
context.report(
context.createError(CoreErrorCodes.TABLE_MISSING_GEOMETRY)
.setMessageParam("FILEPATH", context.relativize(matchingFile))
.setMessageParam("FILEPATH", relativePath)
);
} else if (!missingAttribute.getConstraints().isRequired()) {
context.report(
context.createError(CoreErrorCodes.TABLE_MISSING_NULLABLE_ATTRIBUTE)
.setMessageParam("ATTRIBUTE_NAME", missingAttribute.getName())
.setMessageParam("FILEPATH", context.relativize(matchingFile))
.setMessageParam("FILEPATH", relativePath)
);
} else {
context.report(
context.createError(CoreErrorCodes.TABLE_MISSING_ATTRIBUTE)
.setMessageParam("ATTRIBUTE_NAME", missingAttribute.getName())
.setMessageParam("FILEPATH", context.relativize(matchingFile))
.setMessageParam("FILEPATH", relativePath)
);
}
context.endModel(missingAttribute);
Expand Down
101 changes: 101 additions & 0 deletions validator-core/src/main/java/fr/ign/validator/data/Table.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package fr.ign.validator.data;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;

import fr.ign.validator.Context;
import fr.ign.validator.data.file.MultiTableFile;
import fr.ign.validator.data.file.TableFile;
import fr.ign.validator.error.CoreErrorCodes;
import fr.ign.validator.mapping.FeatureTypeMapper;
import fr.ign.validator.model.FeatureType;
import fr.ign.validator.tools.TableReader;
import fr.ign.validator.validation.Validatable;

/**
* A Table from a {@link TableFile} or a {@link MultiTableFile}
*
* @author MBorne
*/
public class Table implements Validatable {
public static final Logger log = LogManager.getRootLogger();
public static final Marker MARKER = MarkerManager.getMarker("DocumentFile");

/**
* Expected model for the table.
*/
private FeatureType featureType;

/**
* Source file to read table data.
*/
private TableReader reader;

/**
* Path relative to the root of the document, suffixed with an hash for
* MultiTableFile, in order to report errors.
*/
private String relativePath;

public Table(FeatureType featureType, TableReader reader, String relativePath) {
this.featureType = featureType;
this.reader = reader;
this.relativePath = relativePath;
}

@Override
public void validate(Context context) {
context.beginData(this);
doValidate(context);
context.endData(this);
}

public void doValidate(Context context) {
log.info(MARKER, "Validate '{}' according to {}...", relativePath, featureType);
if (!reader.isCharsetValid()) {
log.error(MARKER, "Invalid charset '{}' for '{}'", context.getEncoding(), relativePath);
context.report(
context.createError(CoreErrorCodes.TABLE_UNEXPECTED_ENCODING)
.setMessageParam("ENCODING", context.getEncoding().toString())
);
}

/*
* header validation
*/
String[] columns = reader.getHeader();
FeatureTypeMapper mapping = new FeatureTypeMapper(columns, featureType);
Header header = new Header(relativePath, mapping);
header.validate(context);

/*
* feature validation
*/
int count = 0;
while (reader.hasNext()) {
count++;

Row row = new Row(count, reader.next(), mapping);
row.validate(context);
if (count % 10000 == 0) {
log.debug(MARKER, "'{}' : {} rows processed...", relativePath, count);
}
}

/*
* check for empty file
*/
if (count == 0) {
context.report(
context.createError(CoreErrorCodes.FILE_EMPTY)
.setMessageParam("FILEPATH", relativePath)
);
}

log.info(MARKER, "'{}' : {} row(s) processed", relativePath, count);

}

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package fr.ign.validator.data.file;

import java.io.File;
import java.io.IOException;

import fr.ign.validator.Context;
import fr.ign.validator.data.DocumentFile;
import fr.ign.validator.data.Table;
import fr.ign.validator.error.CoreErrorCodes;
import fr.ign.validator.model.file.EmbeddedTableModel;
import fr.ign.validator.model.file.MultiTableModel;
import fr.ign.validator.tools.MultiTableReader;

public class MultiTableFile extends DocumentFile {

Expand All @@ -20,12 +25,39 @@ public MultiTableModel getFileModel() {
return fileModel;
}

/**
* TODO complete to validate according to {@link MultiTableModel}
*/
@Override
protected void validateContent(Context context) {

try {
log.info(MARKER, "Validate '{}' according to {}...", getPath(), fileModel);
MultiTableReader reader = MultiTableReader.createMultiTableReader(getPath());
for (String tableName : reader.getTableNames()) {
String relativePath = context.relativize(getPath()) + "#" + tableName;
EmbeddedTableModel tableModel = fileModel.getTableModelByName(tableName);
if (tableModel == null || tableModel.getFeatureType() == null) {
// TODO report MULTITABLE_MISSING_MODEL
log.warn(
MARKER, "Validate '{}' according to {} : no FeatureType found for '{}'!",
getPath(),
fileModel,
tableName
);
continue;
}
Table table = new Table(
tableModel.getFeatureType(),
reader.getTableReader(tableName),
relativePath
);
table.validate(context);
}

} catch (IOException e) {
log.error(MARKER, "Fail to read file '{}'!", getPath());
context.report(
context.createError(CoreErrorCodes.FILE_NOT_OPENED)
.setMessageParam("FILEPATH", context.relativize(getPath()))
);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@

import fr.ign.validator.Context;
import fr.ign.validator.data.DocumentFile;
import fr.ign.validator.data.Header;
import fr.ign.validator.data.Row;
import fr.ign.validator.data.Table;
import fr.ign.validator.error.CoreErrorCodes;
import fr.ign.validator.mapping.FeatureTypeMapper;
import fr.ign.validator.model.file.TableModel;
import fr.ign.validator.tools.TableReader;

Expand Down Expand Up @@ -39,48 +37,14 @@ protected void validateContent(Context context) {
*/
protected void validateTable(Context context, TableModel tableModel, File matchingFile) {
try {
log.info(MARKER, "Validate '{}' according to {}...", matchingFile, tableModel);
TableReader reader = TableReader.createTableReader(matchingFile, context.getEncoding());
if (!reader.isCharsetValid()) {
log.error(MARKER, "Invalid charset '{}' for '{}'", context.getEncoding(), matchingFile);
context.report(
context.createError(CoreErrorCodes.TABLE_UNEXPECTED_ENCODING)
.setMessageParam("ENCODING", context.getEncoding().toString())
);
}

/*
* header validation
*/
String[] columns = reader.getHeader();
FeatureTypeMapper mapping = new FeatureTypeMapper(columns, tableModel.getFeatureType());
Header header = new Header(matchingFile, mapping);
header.validate(context);

/*
* feature validation
*/
int count = 0;
while (reader.hasNext()) {
count++;

Row row = new Row(count, reader.next(), mapping);
row.validate(context);
if (count % 10000 == 0) {
log.debug(MARKER, "'{}' : {} rows processed...", matchingFile, count);
}
}

/*
* check for empty file
*/
if (count == 0) {
context.report(
context.createError(CoreErrorCodes.FILE_EMPTY)
.setMessageParam("FILEPATH", context.relativize(matchingFile))
);
}

log.info(MARKER, "'{}' : {} row(s) processed", matchingFile, count);
Table table = new Table(
tableModel.getFeatureType(),
reader,
context.relativize(matchingFile)
);
table.validate(context);
} catch (IOException e) {
log.error(MARKER, "Fail to read file '{}'!", matchingFile);
context.report(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,15 @@ public class CoreErrorCodes {
public static final ErrorCode ATTRIBUTE_CHARACTERS_REPLACED = ErrorCode.valueOf("ATTRIBUTE_CHARACTERS_REPLACED");
public static final ErrorCode ATTRIBUTE_CHARACTERS_ILLEGAL = ErrorCode.valueOf("ATTRIBUTE_CHARACTERS_ILLEGAL");

/**
* Fail to parse field as a JTS Geometry.
*/
public static final ErrorCode ATTRIBUTE_GEOMETRY_INVALID_FORMAT = ErrorCode.valueOf(
"ATTRIBUTE_GEOMETRY_INVALID_FORMAT"
);
/**
* Topology problem for the geometry.
*/
public static final ErrorCode ATTRIBUTE_GEOMETRY_INVALID = ErrorCode.valueOf("ATTRIBUTE_GEOMETRY_INVALID");
public static final ErrorCode ATTRIBUTE_GEOMETRY_INVALID_DATA_EXTENT = ErrorCode.valueOf(
"ATTRIBUTE_GEOMETRY_INVALID_DATA_EXTENT"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ public class GeometryErrorCode extends CodeListValue {
public static GeometryErrorCode TOO_FEW_POINTS = GeometryErrorCode.valueOf("TOO_FEW_POINTS");
public static GeometryErrorCode INVALID_COORDINATE = GeometryErrorCode.valueOf("INVALID_COORDINATE");
public static GeometryErrorCode RING_NOT_CLOSED = GeometryErrorCode.valueOf("RING_NOT_CLOSED");
public static GeometryErrorCode INVALID_WKT = GeometryErrorCode.valueOf("INVALID_WKT");
public static GeometryErrorCode UNKNOWN = GeometryErrorCode.valueOf("UNKNOWN");

private GeometryErrorCode(String value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,4 @@ SELF_INTERSECTION = Un contour int
RING_NOT_CLOSED = La géométrie ne contient pas assez de points (au moins 2 pour une ligne, 3 pour un polygone), contient un contour non-fermé, ou bien contient des coordonnées invalides (valeur NULL) (RING_NOT_CLOSED, INVALID_COORDINATE, TOO_FEW_POINTS, INVALID_WKT).
INVALID_COORDINATE = La géométrie ne contient pas assez de points (au moins 2 pour une ligne, 3 pour un polygone), contient un contour non-fermé, ou bien contient des coordonnées invalides (valeur NULL) (RING_NOT_CLOSED, INVALID_COORDINATE, TOO_FEW_POINTS, INVALID_WKT).
TOO_FEW_POINTS = La géométrie ne contient pas assez de points (au moins 2 pour une ligne, 3 pour un polygone), contient un contour non-fermé, ou bien contient des coordonnées invalides (valeur NULL) (RING_NOT_CLOSED, INVALID_COORDINATE, TOO_FEW_POINTS, INVALID_WKT).
INVALID_WKT = La géométrie ne contient pas assez de points (au moins 2 pour une ligne, 3 pour un polygone), contient un contour non-fermé, ou bien contient des coordonnées invalides (valeur NULL) (RING_NOT_CLOSED, INVALID_COORDINATE, TOO_FEW_POINTS, INVALID_WKT).
UNKNOWN = Erreur géométrique
12 changes: 9 additions & 3 deletions validator-core/src/main/resources/error-code.json
Original file line number Diff line number Diff line change
Expand Up @@ -159,14 +159,20 @@
"message": "La taille de l'attribut ({VALUE_LENGTH}) dépasse la taille limite autorisée ({EXPECTED_LENGTH}).",
"documentation": "Cette erreur se produit lorsqu'un champ contient une valeur dont la longueur dépasse la taille limite autorisée par le modèle"
},
{
"name": "ATTRIBUTE_GEOMETRY_INVALID_FORMAT",
"level": "ERROR",
"message": "La géométrie ne peut pas être lue (coordonnée invalide, type géométrique non supporté, nombre de point insuffisant,...) : {VALUE}",
"documentation": "Cette erreur se produit lorsque le validateur ne parvient pas à lire une géométrie. Ce cas peut se produire sur des WKT invalides ou sur des courbes."
},
{
"name": "ATTRIBUTE_GEOMETRY_INVALID",
"level": "ERROR",
"message": "La géométrie de l'objet n'est pas topologiquement correcte. {TYPE_ERROR}",
"documentation": "Voir la documentation de PostGIS : http://www.postgis.fr/chrome/site/docs/workshop-foss4g/doc/validity.html"
},
{
"name": "ATTRIBUTE_GEOMETRY_INVALID_DATA_EXTENT",
},{"name":
"ATTRIBUTE_GEOMETRY_INVALID_DATA_EXTENT"
,
"level": "ERROR",
"message": "Les coordonnées de la géométrie de l'objet ne correspondent pas à la zone attendue.",
"documentation": ""
Expand Down
Loading

0 comments on commit e541eae

Please sign in to comment.