diff --git a/.idea/inspectionProfiles/SWAGD.xml b/.idea/inspectionProfiles/SWAGD.xml index 31dc846b..e1d2419d 100644 --- a/.idea/inspectionProfiles/SWAGD.xml +++ b/.idea/inspectionProfiles/SWAGD.xml @@ -78,7 +78,9 @@ - + + diff --git a/.idea/misc.xml b/.idea/misc.xml index 8f96fd28..f43837a9 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,19 +1,6 @@ - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/GeoPackage/src/main/java/com/rgi/geopackage/GeoPackage.java b/GeoPackage/src/main/java/com/rgi/geopackage/GeoPackage.java index a0e268a3..e80851e8 100644 --- a/GeoPackage/src/main/java/com/rgi/geopackage/GeoPackage.java +++ b/GeoPackage/src/main/java/com/rgi/geopackage/GeoPackage.java @@ -204,7 +204,7 @@ public GeoPackage(final File file, final VerificationLevel verificationLevel, fi DatabaseUtility.setPragmaForeignKeys(this.databaseConnection, true); DatabaseUtility.setPragmaJournalModeMemory(this.databaseConnection); - // this was moved below setting the pragmas because is starts a transaction and causes setPragmaSynchronousOff to throw an exception + // This was moved below the pragmas because it starts a transaction and causes setPragmaSynchronousOff to throw an exception this.databaseConnection.setAutoCommit(false); this.core = new GeoPackageCore (this.databaseConnection, isNewFile); @@ -465,5 +465,5 @@ public enum OpenMode private final GeoPackageMetadata metadata; private final GeoPackageExtensions extensions; - private static final byte[] GeoPackageSqliteApplicationId = {(byte) 'G', (byte) 'P', (byte) '1', (byte) '0'}; + private static final byte[] GeoPackageSqliteApplicationId = {(byte) 'G', (byte) 'P', (byte) '1', (byte) '2'}; } diff --git a/GeoPackage/src/main/java/com/rgi/geopackage/core/CoreVerifier.java b/GeoPackage/src/main/java/com/rgi/geopackage/core/CoreVerifier.java index 4dd83e9e..734d8440 100644 --- a/GeoPackage/src/main/java/com/rgi/geopackage/core/CoreVerifier.java +++ b/GeoPackage/src/main/java/com/rgi/geopackage/core/CoreVerifier.java @@ -39,7 +39,6 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.RandomAccessFile; -import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.sql.Connection; import java.sql.PreparedStatement; @@ -153,25 +152,26 @@ public void requirement1() throws IOException, AssertionError * Requirement 2 * *
- * A GeoPackage SHALL contain 0x47503130 ("GP10" in ASCII) - * in the application id field of the SQLite database header - * to indicate a GeoPackage version 1.0 file. + * A GeoPackage SHALL contain an appropriate value in the application ID + * field of the SQLite database header to indicate that it is a GeoPackage. + * The value SHALL have a prefix of 0x4750 ("GP" in ASCII). A value of + * 0x47503132 ("GP12" in ASCII) SHALL indicate a GeoPackage version 1.2 file. *
- * @throws AssertionError throws if it fails to meet the specified requirement; + * @throws AssertionError throws if it fails to meet the specified requirement */ @Requirement(reference = "Requirement 2", - text = "A GeoPackage SHALL contain 0x47503130 ('GP10' in ASCII) in the application id field of the SQLite database header to indicate a GeoPackage version 1.0 file.") + text = "A GeoPackage SHALL contain an appropriate value in the application ID field of the SQLite database header to indicate that it is a GeoPackage. The value SHALL have a prefix of 0x4750 (\"GP\" in ASCII). A value of 0x47503132 (\"GP12\" in ASCII) SHALL indicate a GeoPackage version 1.2 file.") public void requirement2() throws AssertionError { - final int sizeOfInt = 4; - final byte[] data = new byte[sizeOfInt]; // 4 bytes in an int + final int sizeOfPrefix = 2; + final byte[] data = new byte[sizeOfPrefix]; // 4 bytes in an int try(RandomAccessFile randomAccessFile = new RandomAccessFile(this.file, "r")) { final long applicationIdByteOffset = 68; randomAccessFile.seek(applicationIdByteOffset); assertTrue("The file does not have enough bytes to contain a GeoPackage.", - randomAccessFile.read(data, 0, sizeOfInt) == sizeOfInt, + randomAccessFile.read(data, 0, sizeOfPrefix) == sizeOfPrefix, Severity.Warning); } catch(final Exception ex) @@ -179,19 +179,11 @@ public void requirement2() throws AssertionError throw new AssertionError(ex, Severity.Error); } - // application id - // http://www.sqlite.org/fileformat2.html - // http://www.geopackage.org/spec/#_sqlite_container - // A GeoPackage SHALL contain 0x47503130 ("GP10" in ASCII) in the - // application id field of the SQLite database header to indicate a - // GeoPackage version 1.0 file. - // The bytes 'G', 'P', '1', '0' are equivalent to 0x47503130 - final int expectedAppId = 0x47503130; - - final int applicationId = ByteBuffer.wrap(data).asIntBuffer().get(); - - assertTrue(String.format("Bad Application ID: 0x%08x Expected: 0x%08x", applicationId, expectedAppId), - applicationId == expectedAppId, + assertTrue(String.format("Bad Application ID prefix: \"%c%c\" Expected: \"GP\"", + data[0], + data[1]), + data[0] == 'G' && + data[1] == 'P', Severity.Warning); } diff --git a/GeoPackage/src/main/java/com/rgi/geopackage/core/GeoPackageCore.java b/GeoPackage/src/main/java/com/rgi/geopackage/core/GeoPackageCore.java index 7507c647..1ba78a1a 100644 --- a/GeoPackage/src/main/java/com/rgi/geopackage/core/GeoPackageCore.java +++ b/GeoPackage/src/main/java/com/rgi/geopackage/core/GeoPackageCore.java @@ -646,7 +646,7 @@ protected void createDefaultTables() throws SQLException // Add the default entries to the spatial reference system table // See: http://www.geopackage.org/spec/#spatial_ref_sys -> 1.1.2.1.2. Table Data Values, Requirement 11 this.addSpatialReferenceSystemNoCommit("World Geodetic System (WGS) 1984", - 1, + 4326, "EPSG", 4326, "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]]", // http://spatialreference.org/ref/epsg/wgs-84/ogcwkt/ diff --git a/GeoPackage/src/main/java/com/rgi/geopackage/extensions/GeoPackageExtensions.java b/GeoPackage/src/main/java/com/rgi/geopackage/extensions/GeoPackageExtensions.java index e3f78cc6..84cc8ae1 100644 --- a/GeoPackage/src/main/java/com/rgi/geopackage/extensions/GeoPackageExtensions.java +++ b/GeoPackage/src/main/java/com/rgi/geopackage/extensions/GeoPackageExtensions.java @@ -23,16 +23,6 @@ package com.rgi.geopackage.extensions; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.sql.Connection; -import java.sql.SQLException; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - import com.rgi.common.util.jdbc.JdbcUtility; import com.rgi.geopackage.GeoPackage; import com.rgi.geopackage.core.GeoPackageCore; @@ -43,6 +33,16 @@ import com.rgi.geopackage.verification.VerificationIssue; import com.rgi.geopackage.verification.VerificationLevel; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * 'Extensions' subsystem of the {@link GeoPackage} implementation * @@ -372,7 +372,7 @@ protected String getExtensionsTableCreationSql() "(table_name TEXT, -- Name of the table that requires the extension. When NULL, the extension is required for the entire GeoPackage. SHALL NOT be NULL when the column_name is not NULL.\n" + " column_name TEXT, -- Name of the column that requires the extension. When NULL, the extension is required for the entire table.\n" + " extension_name TEXT NOT NULL, -- The case sensitive name of the extension that is required, in the form _.\n" + - " definition TEXT NOT NULL, -- Definition of the extension in the form specfied by the template in GeoPackage Extension Template (Normative) or reference thereto.\n" + + " definition TEXT NOT NULL, -- Definition of the extension in the form specified by the template in GeoPackage Extension Template (Normative) or reference thereto.\n" + " scope TEXT NOT NULL, -- Indicates scope of extension effects on readers / writers: read-write or write-only in lowercase.\n" + " CONSTRAINT ge_tce UNIQUE (table_name, column_name, extension_name))"; } diff --git a/GeoPackage/src/main/java/com/rgi/geopackage/features/ColumnDefault.java b/GeoPackage/src/main/java/com/rgi/geopackage/features/ColumnDefault.java index aac4eb1d..96c5099f 100644 --- a/GeoPackage/src/main/java/com/rgi/geopackage/features/ColumnDefault.java +++ b/GeoPackage/src/main/java/com/rgi/geopackage/features/ColumnDefault.java @@ -83,7 +83,7 @@ static ColumnDefault from(final double value) } /** - * Creates a string literal of the form ''. Single quotes in the + * Creates a string literal of the form '<string>'. Single quotes in the * string value will be escaped with an additional single quote e.g. ' * becomes '' * @@ -102,7 +102,7 @@ static ColumnDefault from(final String value) } /** - * Creates a blob literal in the form X'' + * Creates a blob literal in the form X'<hex digits>' * * @param value * Array of bytes representing a binary blob diff --git a/GeoPackage/src/main/java/com/rgi/geopackage/features/FeaturesVerifier.java b/GeoPackage/src/main/java/com/rgi/geopackage/features/FeaturesVerifier.java index 72572b27..d56a21ee 100644 --- a/GeoPackage/src/main/java/com/rgi/geopackage/features/FeaturesVerifier.java +++ b/GeoPackage/src/main/java/com/rgi/geopackage/features/FeaturesVerifier.java @@ -113,7 +113,7 @@ public void requirement18() throws AssertionError { final List missingFeatureTables = this.contentsFeatureTableNames .stream() - .filter(this::hasPrimaryKey) + .filter(tableName -> !this.hasPrimaryKey(tableName)) .collect(Collectors.toList()); assertTrue(String.format("The following feature table entries in the gpkg_contents table are missing or are missing a primary key column: %s", @@ -364,9 +364,9 @@ private boolean isValidGeometry(final byte[] geoPackageBinaryBlob) { try { - final BinaryHeader binaryHeader = new BinaryHeader(geoPackageBinaryBlob); + final BinaryHeader binaryHeader = new BinaryHeader(geoPackageBinaryBlob); // This will throw if the array length is too short to contain a header (or if it's not long enough to contain the envelope type specified) - final Geometry geometry = this.createGeometry(geoPackageBinaryBlob); // correctly encoded per ISO 13249-3 clause 5.1.46 + final Geometry geometry = this.createGeometry(binaryHeader, geoPackageBinaryBlob); // correctly encoded per ISO 13249-3 clause 5.1.46 return binaryHeader.getEnvelopeContentsIndicator() != EnvelopeContentsIndicator.NoEnvelope || binaryHeader.getEnvelope().equals(geometry.createEnvelope()); @@ -377,10 +377,9 @@ private boolean isValidGeometry(final byte[] geoPackageBinaryBlob) } } - private Geometry createGeometry(final byte[] geoPackageBinaryBlob) throws WellKnownBinaryFormatException + private Geometry createGeometry(final BinaryHeader binaryHeader, + final byte[] geoPackageBinaryBlob) throws WellKnownBinaryFormatException { - final BinaryHeader binaryHeader = new BinaryHeader(geoPackageBinaryBlob); // This will throw if the array length is too short to contain a header (or if it's not long enough to contain the envelope type specified) - if(binaryHeader.getBinaryType() == BinaryType.Standard) { final int headerByteLength = binaryHeader.getByteSize(); diff --git a/GeoPackage/src/main/java/com/rgi/geopackage/features/GeoPackageFeatures.java b/GeoPackage/src/main/java/com/rgi/geopackage/features/GeoPackageFeatures.java index 7e65aa18..75721931 100644 --- a/GeoPackage/src/main/java/com/rgi/geopackage/features/GeoPackageFeatures.java +++ b/GeoPackage/src/main/java/com/rgi/geopackage/features/GeoPackageFeatures.java @@ -854,17 +854,17 @@ protected void addGeometryColumnNoCommit(final String tableNam { this.createGeometryColumnTableNoCommit(); // Create the feature metadata table - final String insertTileMatrix = String.format("INSERT INTO %s (%s, %s, %s, %s, %s, %s) VALUES (?, ?, ?, ?, ?, ?)", - GeoPackageFeatures.GeometryColumnsTableName, - "table_name", - "column_name", - "geometry_type_name", - "srs_id", - "z", - "m"); + final String insertGeometryColumn = String.format("INSERT INTO %s (%s, %s, %s, %s, %s, %s) VALUES (?, ?, ?, ?, ?, ?)", + GeoPackageFeatures.GeometryColumnsTableName, + "table_name", + "column_name", + "geometry_type_name", + "srs_id", + "z", + "m"); JdbcUtility.update(this.databaseConnection, - insertTileMatrix, + insertGeometryColumn, preparedStatement -> { preparedStatement.setString(1, tableName); preparedStatement.setString(2, geometryColumn.getName()); preparedStatement.setString(3, geometryColumn.getType()); diff --git a/GeoPackage/src/main/java/com/rgi/geopackage/metadata/GeoPackageMetadata.java b/GeoPackage/src/main/java/com/rgi/geopackage/metadata/GeoPackageMetadata.java index e8d705d2..a7203602 100644 --- a/GeoPackage/src/main/java/com/rgi/geopackage/metadata/GeoPackageMetadata.java +++ b/GeoPackage/src/main/java/com/rgi/geopackage/metadata/GeoPackageMetadata.java @@ -23,6 +23,13 @@ package com.rgi.geopackage.metadata; +import com.rgi.common.util.jdbc.ResultSetStream; +import com.rgi.geopackage.utility.DatabaseUtility; +import com.rgi.geopackage.utility.SelectBuilder; +import com.rgi.geopackage.verification.VerificationIssue; +import com.rgi.geopackage.verification.VerificationLevel; + +import javax.activation.MimeType; import java.net.URI; import java.sql.Connection; import java.sql.PreparedStatement; @@ -37,14 +44,6 @@ import java.util.Objects; import java.util.stream.Collectors; -import javax.activation.MimeType; - -import com.rgi.common.util.jdbc.ResultSetStream; -import com.rgi.geopackage.utility.DatabaseUtility; -import com.rgi.geopackage.utility.SelectBuilder; -import com.rgi.geopackage.verification.VerificationIssue; -import com.rgi.geopackage.verification.VerificationLevel; - /** * @author Luke Lambert * @@ -493,12 +492,12 @@ protected String getMetadataTableCreationSql() // http://www.geopackage.org/spec/#gpkg_metadata_cols // http://www.geopackage.org/spec/#gpkg_metadata_sql return "CREATE TABLE " + GeoPackageMetadata.MetadataTableName + "\n" + - "(id INTEGER CONSTRAINT m_pk PRIMARY KEY ASC NOT NULL UNIQUE, -- Metadata primary key\n" + - " md_scope TEXT NOT NULL DEFAULT 'dataset', -- Case sensitive name of the data scope to which this metadata applies; see Metadata Scopes\n" + - " md_standard_uri TEXT NOT NULL, -- URI reference to the metadata structure definition authority\n" + - " mime_type TEXT NOT NULL DEFAULT 'text/xml', -- MIME encoding of metadata\n" + - " metadata TEXT NOT NULL -- metadata\n" + - ");"; + "(id INTEGER CONSTRAINT m_pk PRIMARY KEY ASC NOT NULL UNIQUE, -- Metadata primary key\n" + + " md_scope TEXT NOT NULL DEFAULT 'dataset', -- Case sensitive name of the data scope to which this metadata applies; see Metadata Scopes\n" + + " md_standard_uri TEXT NOT NULL, -- URI reference to the metadata structure definition authority\n" + + " mime_type TEXT NOT NULL DEFAULT 'text/xml', -- MIME encoding of metadata\n" + + " metadata TEXT NOT NULL -- metadata\n" + + ");"; } @SuppressWarnings("static-method") @@ -507,15 +506,15 @@ protected String getMetadataReferenceTableCreationSql() // http://www.geopackage.org/spec/#gpkg_metadata_reference_cols // http://www.geopackage.org/spec/#gpkg_metadata_reference_sql return "CREATE TABLE " + GeoPackageMetadata.MetadataReferenceTableName + "\n" + - "(reference_scope TEXT NOT NULL, -- Lowercase metadata reference scope; one of 'geopackage', 'table','column', 'row', 'row/col'\n" + - " table_name TEXT, -- Name of the table to which this metadata reference applies, or NULL for reference_scope of 'geopackage'\n" + - " column_name TEXT, -- Name of the column to which this metadata reference applies; NULL for reference_scope of 'geopackage','table' or 'row', or the name of a column in the table_name table for reference_scope of 'column' or 'row/col'\n" + - " row_id_value INTEGER, -- NULL for reference_scope of 'geopackage', 'table' or 'column', or the rowed of a row record in the table_name table for reference_scope of 'row' or 'row/col'\n" + - " timestamp DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')), -- timestamp value in ISO 8601 format as defined by the strftime function '%Y-%m-%dT%H:%M:%fZ' format string applied to the current time\n" + - " md_file_id INTEGER NOT NULL, -- gpkg_metadata table id column value for the metadata to which this gpkg_metadata_reference applies\n" + - " md_parent_id INTEGER, -- gpkg_metadata table id column value for the hierarchical parent gpkg_metadata for the gpkg_metadata to which this gpkg_metadata_reference applies, or NULL if md_file_id forms the root of a metadata hierarchy\n" + - " CONSTRAINT crmr_mfi_fk FOREIGN KEY (md_file_id) REFERENCES gpkg_metadata(id),\n" + - " CONSTRAINT crmr_mpi_fk FOREIGN KEY (md_parent_id) REFERENCES gpkg_metadata(id));"; + "(reference_scope TEXT NOT NULL, -- Lowercase metadata reference scope; one of 'geopackage', 'table','column', 'row', 'row/col'\n" + + " table_name TEXT, -- Name of the table to which this metadata reference applies, or NULL for reference_scope of 'geopackage'\n" + + " column_name TEXT, -- Name of the column to which this metadata reference applies; NULL for reference_scope of 'geopackage','table' or 'row', or the name of a column in the table_name table for reference_scope of 'column' or 'row/col'\n" + + " row_id_value INTEGER, -- NULL for reference_scope of 'geopackage', 'table' or 'column', or the rowed of a row record in the table_name table for reference_scope of 'row' or 'row/col'\n" + + " timestamp DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')), -- timestamp value in ISO 8601 format as defined by the strftime function '%Y-%m-%dT%H:%M:%fZ' format string applied to the current time\n" + + " md_file_id INTEGER NOT NULL, -- gpkg_metadata table id column value for the metadata to which this gpkg_metadata_reference applies\n" + + " md_parent_id INTEGER, -- gpkg_metadata table id column value for the hierarchical parent gpkg_metadata for the gpkg_metadata to which this gpkg_metadata_reference applies, or NULL if md_file_id forms the root of a metadata hierarchy\n" + + " CONSTRAINT crmr_mfi_fk FOREIGN KEY (md_file_id) REFERENCES gpkg_metadata(id),\n" + + " CONSTRAINT crmr_mpi_fk FOREIGN KEY (md_parent_id) REFERENCES gpkg_metadata(id));"; } /** diff --git a/GeoPackage/src/main/java/com/rgi/geopackage/utility/DatabaseUtility.java b/GeoPackage/src/main/java/com/rgi/geopackage/utility/DatabaseUtility.java index d8285db8..039a129f 100644 --- a/GeoPackage/src/main/java/com/rgi/geopackage/utility/DatabaseUtility.java +++ b/GeoPackage/src/main/java/com/rgi/geopackage/utility/DatabaseUtility.java @@ -279,7 +279,7 @@ public static void validateTableName(final String tableName) throw new IllegalArgumentException("The table name must begin with a letter (A..Z, a..z) or an underscore (_) and may only be followed by letters, underscores, or numbers"); } - if(tableName.startsWith("gpkg_")) + if(tableName.toLowerCase().startsWith("gpkg_")) { throw new IllegalArgumentException("The table name may not start with the reserved prefix 'gpkg_'"); }