You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by js...@apache.org on 2023/11/20 13:44:59 UTC
(sis) branch geoapi-4.0 updated: feat(Shapefile): fuse DBFField and DBFFieldEncoder, prepare store writing support
This is an automated email from the ASF dual-hosted git repository.
jsorel pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
new ceff560423 feat(Shapefile): fuse DBFField and DBFFieldEncoder, prepare store writing support
ceff560423 is described below
commit ceff560423fd5352aa0a875e9c3be66776ccf969
Author: jsorel <jo...@geomatys.com>
AuthorDate: Mon Nov 20 14:44:29 2023 +0100
feat(Shapefile): fuse DBFField and DBFFieldEncoder, prepare store writing support
---
.../sis/storage/shapefile/ShapefileStore.java | 38 ++-
.../apache/sis/storage/shapefile/dbf/DBFField.java | 258 ++++++++++++++++++---
.../sis/storage/shapefile/dbf/DBFFieldEncoder.java | 251 --------------------
.../sis/storage/shapefile/dbf/DBFHeader.java | 7 +-
.../sis/storage/shapefile/dbf/DBFReader.java | 4 +-
.../sis/storage/shapefile/dbf/DBFWriter.java | 2 +-
.../sis/storage/shapefile/ShapefileStoreTest.java | 37 +++
.../sis/storage/shapefile/dbf/DBFIOTest.java | 13 +-
8 files changed, 313 insertions(+), 297 deletions(-)
diff --git a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ShapefileStore.java b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ShapefileStore.java
index 37c24a9615..eed006d9bd 100644
--- a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ShapefileStore.java
+++ b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ShapefileStore.java
@@ -108,7 +108,7 @@ import org.opengis.util.CodeList;
*
* @author Johann Sorel (Geomatys)
*/
-public final class ShapefileStore extends DataStore implements FeatureSet {
+public final class ShapefileStore extends DataStore implements WritableFeatureSet {
private static final String GEOMETRY_NAME = "geometry";
@@ -175,6 +175,26 @@ public final class ShapefileStore extends DataStore implements FeatureSet {
return featureSetView.getEnvelope();
}
+ @Override
+ public void updateType(FeatureType featureType) throws DataStoreException {
+ featureSetView.updateType(featureType);
+ }
+
+ @Override
+ public void add(Iterator<? extends Feature> iterator) throws DataStoreException {
+ featureSetView.add(iterator);
+ }
+
+ @Override
+ public void removeIf(Predicate<? super Feature> predicate) throws DataStoreException {
+ featureSetView.removeIf(predicate);
+ }
+
+ @Override
+ public void replaceIf(Predicate<? super Feature> predicate, UnaryOperator<Feature> unaryOperator) throws DataStoreException {
+ featureSetView.replaceIf(predicate, unaryOperator);
+ }
+
private class AsFeatureSet extends AbstractFeatureSet implements WritableFeatureSet {
private final Rectangle2D.Double filter;
@@ -205,6 +225,13 @@ public final class ShapefileStore extends DataStore implements FeatureSet {
this.dbfProperties = properties;
}
+ /**
+ * @return true if this view reads all data without any filter.
+ */
+ private boolean isDefaultView() {
+ return filter == null && dbfProperties == null && readShp;
+ }
+
@Override
public synchronized FeatureType getType() throws DataStoreException {
if (type == null) {
@@ -279,7 +306,7 @@ public final class ShapefileStore extends DataStore implements FeatureSet {
dbfPropertiesIndex[idx] = i;
idx++;
- final AttributeTypeBuilder atb = ftb.addAttribute(field.getEncoder().getValueClass()).setName(field.fieldName);
+ final AttributeTypeBuilder atb = ftb.addAttribute(field.valueClass).setName(field.fieldName);
//no official but 'id' field is common
if (!hasId && "id".equalsIgnoreCase(field.fieldName) || "identifier".equalsIgnoreCase(field.fieldName)) {
idField = field.fieldName;
@@ -523,21 +550,28 @@ public final class ShapefileStore extends DataStore implements FeatureSet {
@Override
public void updateType(FeatureType newType) throws DataStoreException {
+ if (!isDefaultView()) throw new DataStoreException("Resource not writable in current filter state");
+ if (Files.exists(shpPath)) {
+ throw new DataStoreException("Update type is possible only when files do not exist. It can be used to create a new shapefile but not to update one.");
+ }
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void add(Iterator<? extends Feature> features) throws DataStoreException {
+ if (!isDefaultView()) throw new DataStoreException("Resource not writable in current filter state");
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void removeIf(Predicate<? super Feature> filter) throws DataStoreException {
+ if (!isDefaultView()) throw new DataStoreException("Resource not writable in current filter state");
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void replaceIf(Predicate<? super Feature> filter, UnaryOperator<Feature> updater) throws DataStoreException {
+ if (!isDefaultView()) throw new DataStoreException("Resource not writable in current filter state");
throw new UnsupportedOperationException("Not supported yet.");
}
}
diff --git a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/DBFField.java b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/DBFField.java
index 308eaef438..1465e84060 100644
--- a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/DBFField.java
+++ b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/DBFField.java
@@ -19,6 +19,9 @@ package org.apache.sis.storage.shapefile.dbf;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
+import java.text.NumberFormat;
+import java.time.LocalDate;
+import java.util.Locale;
import org.apache.sis.io.stream.ChannelDataInput;
import org.apache.sis.io.stream.ChannelDataOutput;
@@ -80,39 +83,64 @@ public final class DBFField {
*/
public static final int TYPE_OLE = 'g';
- public String fieldName;
- public char fieldType;
- public int fieldAddress;
- public int fieldLength;
- public int fieldLDecimals;
+ public final String fieldName;
+ public final char fieldType;
+ public final int fieldAddress;
+ public final int fieldLength;
+ public final int fieldDecimals;
+ public final Charset charset;
+ public final Class valueClass;
- private DBFFieldEncoder encoder;
+ private final ReadMethod reader;
+ private final WriteMethod writer;
+ //used by decimal format only;
+ private NumberFormat format;
- public DBFField() {
- }
+ public DBFField(String fieldName, char fieldType, int fieldAddress, int fieldLength, int fieldDecimals, Charset charset) {
+ this.fieldName = fieldName;
+ this.fieldType = fieldType;
+ this.fieldAddress = fieldAddress;
+ this.fieldLength = fieldLength;
+ this.fieldDecimals = fieldDecimals;
+ this.charset = charset;
- public DBFField(DBFField toCopy) {
- this.fieldName = toCopy.fieldName;
- this.fieldType = toCopy.fieldType;
- this.fieldAddress = toCopy.fieldAddress;
- this.fieldLength = toCopy.fieldLength;
- this.fieldLDecimals = toCopy.fieldLDecimals;
- this.encoder = toCopy.encoder;
+ switch (Character.toLowerCase(fieldType)) {
+ case TYPE_BINARY : valueClass = Long.class; reader = this::readBinary; writer = this::writeBinary; break;
+ case TYPE_CHAR : valueClass = String.class; reader = this::readChar; writer = this::writeChar; break;
+ case TYPE_DATE : valueClass = LocalDate.class; reader = this::readDate; writer = this::writeDate; break;
+ case TYPE_NUMBER : {
+ if (fieldDecimals != 0) { valueClass = Double.class; reader = this::readNumber; writer = this::writeNumber;
+ format = NumberFormat.getNumberInstance(Locale.US);
+ format.setMaximumFractionDigits(fieldDecimals);
+ format.setMinimumFractionDigits(fieldDecimals);
+ }
+ else if (fieldLength > 9) {valueClass = Long.class; reader = this::readNumberLong; writer = this::writeNumberLong;}
+ else { valueClass = Integer.class; reader = this::readNumberInt; writer = this::writeNumberInt;}
+ break;
+ }
+ case TYPE_LOGIC : valueClass = Boolean.class; reader = this::readLogic; writer = this::writeLogic; break;
+ case TYPE_MEMO : valueClass = Object.class; reader = this::readMemo; writer = this::writeMemo; break;
+ case TYPE_TIMESTAMP : valueClass = Object.class; reader = this::readTimeStamp; writer = this::writeTimeStamp; break;
+ case TYPE_LONG : valueClass = Object.class; reader = this::readLong; writer = this::writeLong; break;
+ case TYPE_INC : valueClass = Object.class; reader = this::readAutoIncrement; writer = this::writeAutoIncrement; break;
+ case TYPE_FLOAT : valueClass = Object.class; reader = this::readFloat; writer = this::writeFloat; break;
+ case TYPE_DOUBLE : valueClass = Object.class; reader = this::readDouble; writer = this::writeDouble; break;
+ case TYPE_OLE : valueClass = Object.class; reader = this::readOLE; writer = this::writeOLE; break;
+ default: throw new IllegalArgumentException("Unknown field type " + fieldType);
+ }
}
- public void read(ChannelDataInput channel, Charset charset) throws IOException {
+ public static DBFField read(ChannelDataInput channel, Charset charset) throws IOException {
byte[] n = channel.readBytes(11);
int nameSize = 0;
for (int i = 0; i < n.length && n[i] != 0; i++,nameSize++);
-
- fieldName = new String(n, 0, nameSize);
- fieldType = Character.valueOf(((char)channel.readUnsignedByte())).toString().charAt(0);
- fieldAddress = channel.readInt();
- fieldLength = channel.readUnsignedByte();
- fieldLDecimals = channel.readUnsignedByte();
+ final String fieldName = new String(n, 0, nameSize);
+ final char fieldType = Character.valueOf(((char)channel.readUnsignedByte())).toString().charAt(0);
+ final int fieldAddress = channel.readInt();
+ final int fieldLength = channel.readUnsignedByte();
+ final int fieldDecimals = channel.readUnsignedByte();
channel.skipBytes(14);
-
- encoder = DBFFieldEncoder.getEncoder(fieldType, fieldLength, fieldLDecimals, charset);
+ return new DBFField(fieldName, fieldType, fieldAddress, fieldLength, fieldDecimals, charset);
}
public void write(ChannelDataOutput channel) throws IOException {
@@ -123,12 +151,177 @@ public final class DBFField {
channel.writeByte(fieldType);
channel.writeInt(fieldAddress);
channel.writeByte(fieldLength);
- channel.writeByte(fieldLDecimals);
+ channel.writeByte(fieldDecimals);
channel.repeat(14, (byte) 0);
}
- public DBFFieldEncoder getEncoder() {
- return encoder;
+ public Object readValue(ChannelDataInput channel) throws IOException {
+ return reader.readValue(channel);
+ }
+
+ public void writeValue(ChannelDataOutput channel, Object value) throws IOException {
+ writer.writeValue(channel, value);
+ }
+
+ private Object readBinary(ChannelDataInput channel) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ private Object readChar(ChannelDataInput channel) throws IOException {
+ return new String(channel.readBytes(fieldLength), charset).trim();
+ }
+
+ private Object readDate(ChannelDataInput channel) throws IOException {
+ final String str = new String(channel.readBytes(fieldLength)).trim();
+ final int year = Integer.parseUnsignedInt(str,0,4,10);
+ final int month = Integer.parseUnsignedInt(str,4,6,10);
+ final int day = Integer.parseUnsignedInt(str,6,8,10);
+ return LocalDate.of(year, month, day);
+ }
+
+ private Object readNumber(ChannelDataInput channel) throws IOException {
+ final String str = new String(channel.readBytes(fieldLength)).trim();
+ if (str.isEmpty()) return 0L;
+ else return Double.parseDouble(str);
+ }
+
+ private Object readNumberInt(ChannelDataInput channel) throws IOException {
+ final String str = new String(channel.readBytes(fieldLength)).trim();
+ if (str.isEmpty()) return 0;
+ else return Integer.parseInt(str);
+ }
+
+ private Object readNumberLong(ChannelDataInput channel) throws IOException {
+ final String str = new String(channel.readBytes(fieldLength)).trim();
+ if (str.isEmpty()) return 0L;
+ else return Long.parseLong(str);
+ }
+
+ private Object readLogic(ChannelDataInput channel) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ private Object readMemo(ChannelDataInput channel) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ private Object readTimeStamp(ChannelDataInput channel) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ private Object readLong(ChannelDataInput channel) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ private Object readAutoIncrement(ChannelDataInput channel) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ private Object readFloat(ChannelDataInput channel) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ private Object readDouble(ChannelDataInput channel) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ private Object readOLE(ChannelDataInput channel) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ private void writeBinary(ChannelDataOutput channel, Object value) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ private void writeChar(ChannelDataOutput channel, Object value) throws IOException {
+ final String txt = (String) value;
+ final byte[] bytes = txt.getBytes(charset);
+ if (bytes.length >= fieldLength) {
+ channel.write(bytes, 0, fieldLength);
+ } else {
+ channel.write(bytes);
+ channel.repeat(fieldLength - bytes.length, (byte)' ');
+ }
+ }
+
+ private void writeDate(ChannelDataOutput channel, Object value) throws IOException {
+ final LocalDate date = (LocalDate) value;
+ final StringBuilder sb = new StringBuilder();
+ String year = Integer.toString(date.getYear());
+ String month = Integer.toString(date.getMonthValue());
+ String day = Integer.toString(date.getDayOfMonth());
+ switch (year.length()) {
+ case 1: sb.append("000"); break;
+ case 2: sb.append("00"); break;
+ case 3: sb.append("0"); break;
+ }
+ sb.append(year);
+ if(month.length() < 2) sb.append("0");
+ sb.append(month);
+ if(day.length() < 2) sb.append("0");
+ sb.append(day);
+ channel.repeat(fieldLength - sb.length(), (byte)' ');
+ channel.write(sb.toString().getBytes(StandardCharsets.US_ASCII));
+ }
+
+ private void writeNumber(ChannelDataOutput channel, Object value) throws IOException {
+ final Number v = ((Number) value);
+ final String str = format.format(v.doubleValue());
+ final int length = str.length();
+ channel.repeat(fieldLength - length, (byte)' ');
+ channel.write(str.getBytes(StandardCharsets.US_ASCII));
+ }
+
+ private void writeNumberInt(ChannelDataOutput channel, Object value) throws IOException {
+ final String v = ((Integer) value).toString();
+ final int length = v.length();
+ if (length > fieldLength) {
+ throw new IOException(v + " is longer then field length " + fieldLength);
+ }
+ channel.repeat(fieldLength - length, (byte)' ');
+ channel.write(v.getBytes(StandardCharsets.US_ASCII));
+ }
+
+ private void writeNumberLong(ChannelDataOutput channel, Object value) throws IOException {
+ final String v = ((Long) value).toString();
+ final int length = v.length();
+ if (length > fieldLength) {
+ throw new IOException(v + " is longer then field length " + fieldLength);
+ }
+ channel.repeat(fieldLength - length, (byte)' ');
+ channel.write(v.getBytes(StandardCharsets.US_ASCII));
+ }
+
+ private void writeLogic(ChannelDataOutput channel, Object value) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ private void writeMemo(ChannelDataOutput channel, Object value) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ private void writeTimeStamp(ChannelDataOutput channel, Object value) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ private void writeLong(ChannelDataOutput channel, Object value) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ private void writeAutoIncrement(ChannelDataOutput channel, Object value) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ private void writeFloat(ChannelDataOutput channel, Object value) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ private void writeDouble(ChannelDataOutput channel, Object value) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ private void writeOLE(ChannelDataOutput channel, Object value) throws IOException {
+ throw new UnsupportedOperationException();
}
@Override
@@ -138,7 +331,16 @@ public final class DBFField {
", fieldType=" + fieldType +
", fieldAddress=" + fieldAddress +
", fieldLength=" + fieldLength +
- ", fieldLDecimals=" + fieldLDecimals +
+ ", fieldLDecimals=" + fieldDecimals +
'}';
}
+
+ private interface ReadMethod {
+ Object readValue(ChannelDataInput channel) throws IOException;
+ }
+
+ private interface WriteMethod {
+ void writeValue(ChannelDataOutput channel, Object value) throws IOException;
+ }
+
}
diff --git a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/DBFFieldEncoder.java b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/DBFFieldEncoder.java
deleted file mode 100644
index bef9167aa5..0000000000
--- a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/DBFFieldEncoder.java
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.apache.sis.storage.shapefile.dbf;
-
-import java.io.IOException;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.text.NumberFormat;
-import java.time.LocalDate;
-import java.util.Locale;
-import org.apache.sis.io.stream.ChannelDataInput;
-import org.apache.sis.io.stream.ChannelDataOutput;
-import static org.apache.sis.storage.shapefile.dbf.DBFField.*;
-
-/**
- *
- * @author Johann Sorel (Geomatys)
- */
-public abstract class DBFFieldEncoder {
-
- public static DBFFieldEncoder getEncoder(char fieldType, int fieldLength, int fieldDecimals, Charset charset) {
- switch (Character.toLowerCase(fieldType)) {
- case TYPE_BINARY : return new Binary(fieldLength, fieldDecimals);
- case TYPE_CHAR : return new Char(fieldLength, fieldDecimals, charset);
- case TYPE_DATE : return new Date(fieldLength, fieldDecimals);
- case TYPE_NUMBER : {
- if (fieldDecimals != 0) return new Decimal(fieldLength, fieldDecimals);
- return fieldLength > 9 ? new LongInt(fieldLength) : new ShortInt(fieldLength);
- }
- case TYPE_LOGIC : return new Logic(fieldLength);
- case TYPE_MEMO : throw new UnsupportedOperationException("todo");
- case TYPE_TIMESTAMP : throw new UnsupportedOperationException("todo");
- case TYPE_LONG : throw new UnsupportedOperationException("todo");
- case TYPE_INC : throw new UnsupportedOperationException("todo");
- case TYPE_FLOAT : throw new UnsupportedOperationException("todo");
- case TYPE_DOUBLE : throw new UnsupportedOperationException("todo");
- case TYPE_OLE : throw new UnsupportedOperationException("todo");
- default: throw new IllegalArgumentException("Unknown field type "+fieldType);
- }
-
- }
-
- protected final Class valueClass;
- protected final int fieldLength;
- protected final int fieldLDecimals;
-
- public DBFFieldEncoder(Class valueClass, int fieldLength, int fieldLDecimals) {
- this.valueClass = valueClass;
- this.fieldLength = fieldLength;
- this.fieldLDecimals = fieldLDecimals;
- }
-
-
- public Class getValueClass() {
- return valueClass;
- }
-
- public abstract Object read(ChannelDataInput channel) throws IOException;
-
- public abstract void write(ChannelDataOutput channel, Object value) throws IOException;
-
-
- private static final class Binary extends DBFFieldEncoder {
-
- public Binary(int fieldLength, int fieldDecimals) {
- super(Long.class, fieldLength, fieldDecimals);
- }
-
- @Override
- public Object read(ChannelDataInput channel) throws IOException {
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
- @Override
- public void write(ChannelDataOutput channel, Object value) throws IOException {
- throw new UnsupportedOperationException("Not supported yet.");
- }
- }
-
- private static final class Char extends DBFFieldEncoder {
-
- private final Charset charset;
-
- public Char(int fieldLength, int fieldDecimals, Charset charset) {
- super(String.class, fieldLength, fieldDecimals);
- this.charset = charset;
- }
-
- @Override
- public Object read(ChannelDataInput channel) throws IOException {
- return new String(channel.readBytes(fieldLength), charset).trim();
- }
-
- @Override
- public void write(ChannelDataOutput channel, Object value) throws IOException {
- final String txt = (String) value;
- final byte[] bytes = txt.getBytes(charset);
- if (bytes.length >= fieldLength) {
- channel.write(bytes, 0, fieldLength);
- } else {
- channel.write(bytes);
- channel.repeat(fieldLength - bytes.length, (byte)' ');
- }
- }
- }
-
- private static final class Date extends DBFFieldEncoder {
-
- public Date(int fieldLength, int fieldDecimals) {
- super(LocalDate.class, fieldLength, fieldDecimals);
- }
-
- @Override
- public Object read(ChannelDataInput channel) throws IOException {
- final String str = new String(channel.readBytes(fieldLength)).trim();
- final int year = Integer.parseUnsignedInt(str,0,4,10);
- final int month = Integer.parseUnsignedInt(str,4,6,10);
- final int day = Integer.parseUnsignedInt(str,6,8,10);
- return LocalDate.of(year, month, day);
- }
-
- @Override
- public void write(ChannelDataOutput channel, Object value) throws IOException {
- final LocalDate date = (LocalDate) value;
- final StringBuilder sb = new StringBuilder();
- String year = Integer.toString(date.getYear());
- String month = Integer.toString(date.getMonthValue());
- String day = Integer.toString(date.getDayOfMonth());
- switch (year.length()) {
- case 1: sb.append("000"); break;
- case 2: sb.append("00"); break;
- case 3: sb.append("0"); break;
- }
- sb.append(year);
- if(month.length() < 2) sb.append("0");
- sb.append(month);
- if(day.length() < 2) sb.append("0");
- sb.append(day);
- channel.repeat(fieldLength - sb.length(), (byte)' ');
- channel.write(sb.toString().getBytes(StandardCharsets.US_ASCII));
- }
- }
-
- private static final class ShortInt extends DBFFieldEncoder {
-
- public ShortInt(int fieldLength) {
- super(Integer.class, fieldLength, 0);
- }
-
- @Override
- public Object read(ChannelDataInput channel) throws IOException {
- final String str = new String(channel.readBytes(fieldLength)).trim();
- if (str.isEmpty()) return 0;
- else return Integer.parseInt(str);
- }
-
- @Override
- public void write(ChannelDataOutput channel, Object value) throws IOException {
- final String v = ((Integer) value).toString();
- final int length = v.length();
- if (length > fieldLength) {
- throw new IOException(v + " is longer then field length " + fieldLength);
- }
- channel.repeat(fieldLength - length, (byte)' ');
- channel.write(v.getBytes(StandardCharsets.US_ASCII));
- }
- }
-
- private static final class LongInt extends DBFFieldEncoder {
-
- public LongInt(int fieldLength) {
- super(Long.class, fieldLength, 0);
- }
-
- @Override
- public Object read(ChannelDataInput channel) throws IOException {
- final String str = new String(channel.readBytes(fieldLength)).trim();
- if (str.isEmpty()) return 0L;
- else return Long.parseLong(str);
- }
-
- @Override
- public void write(ChannelDataOutput channel, Object value) throws IOException {
- final String v = ((Long) value).toString();
- final int length = v.length();
- if (length > fieldLength) {
- throw new IOException(v + " is longer then field length " + fieldLength);
- }
- channel.repeat(fieldLength - length, (byte)' ');
- channel.write(v.getBytes(StandardCharsets.US_ASCII));
- }
- }
-
- private static final class Decimal extends DBFFieldEncoder {
- private final NumberFormat format = NumberFormat.getNumberInstance(Locale.US);
-
- public Decimal(int fieldLength, int fieldDecimals) {
- super(Double.class, fieldLength, fieldDecimals);
- format.setMaximumFractionDigits(fieldDecimals);
- format.setMinimumFractionDigits(fieldDecimals);
- }
-
- @Override
- public Object read(ChannelDataInput channel) throws IOException {
- final String str = new String(channel.readBytes(fieldLength)).trim();
- if (str.isEmpty()) return 0L;
- else return Double.parseDouble(str);
- }
-
- @Override
- public void write(ChannelDataOutput channel, Object value) throws IOException {
- final Number v = ((Number) value);
- final String str =format.format(v.doubleValue());
- final int length = str.length();
- channel.repeat(fieldLength - length, (byte)' ');
- channel.write(str.getBytes(StandardCharsets.US_ASCII));
- }
- }
-
- private static final class Logic extends DBFFieldEncoder {
-
- public Logic(int fieldLength) {
- super(Boolean.class, fieldLength, 0);
- }
-
- @Override
- public Object read(ChannelDataInput channel) throws IOException {
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
- @Override
- public void write(ChannelDataOutput channel, Object value) throws IOException {
- throw new UnsupportedOperationException("Not supported yet.");
- }
- }
-
-}
diff --git a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/DBFHeader.java b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/DBFHeader.java
index 67c95ec025..6a04d3e156 100644
--- a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/DBFHeader.java
+++ b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/DBFHeader.java
@@ -51,9 +51,7 @@ public final class DBFHeader {
this.headerSize = toCopy.headerSize;
this.recordSize = toCopy.recordSize;
this.fields = new DBFField[toCopy.fields.length];
- for (int i = 0; i < this.fields.length; i++) {
- this.fields[i] = new DBFField(toCopy.fields[i]);
- }
+ System.arraycopy(toCopy.fields, 0, this.fields, 0, this.fields.length);
}
/**
@@ -82,8 +80,7 @@ public final class DBFHeader {
fields = new DBFField[(headerSize - FIELD_SIZE - 1) / FIELD_SIZE];
for (int i = 0; i < fields.length; i++) {
- fields[i] = new DBFField();
- fields[i].read(channel, charset);
+ fields[i] = DBFField.read(channel, charset);
}
if (channel.readByte()!= FIELD_DESCRIPTOR_TERMINATOR) {
throw new IOException("Unvalid database III field descriptor terminator");
diff --git a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/DBFReader.java b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/DBFReader.java
index 4f61f8456f..e512fad005 100644
--- a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/DBFReader.java
+++ b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/DBFReader.java
@@ -87,14 +87,14 @@ public final class DBFReader implements AutoCloseable {
//read all fields
record.fields = new Object[header.fields.length];
for (int i = 0; i < header.fields.length; i++) {
- record.fields[i] = header.fields[i].getEncoder().read(channel);
+ record.fields[i] = header.fields[i].readValue(channel);
}
} else {
//read only selected fields
record.fields = new Object[fieldsToRead.length];
for (int i = 0,k = 0; i < header.fields.length; i++) {
if (k < fieldsToRead.length && fieldsToRead[k] == i) {
- record.fields[k++] = header.fields[i].getEncoder().read(channel);
+ record.fields[k++] = header.fields[i].readValue(channel);
} else {
//skip this field
channel.skipBytes(header.fields[i].fieldLength);
diff --git a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/DBFWriter.java b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/DBFWriter.java
index b5b8ba507e..0b04a379c8 100644
--- a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/DBFWriter.java
+++ b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/DBFWriter.java
@@ -45,7 +45,7 @@ public final class DBFWriter implements AutoCloseable{
public void write(DBFRecord record) throws IOException {
channel.writeByte(DBFReader.TAG_PRESENT);
for (int i = 0; i < header.fields.length; i++) {
- header.fields[i].getEncoder().write(channel, record.fields[i]);
+ header.fields[i].writeValue(channel, record.fields[i]);
}
writtenNbRecord++;
}
diff --git a/incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/ShapefileStoreTest.java b/incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/ShapefileStoreTest.java
index ab490e7cfd..cfa9d2645e 100644
--- a/incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/ShapefileStoreTest.java
+++ b/incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/ShapefileStoreTest.java
@@ -160,4 +160,41 @@ public class ShapefileStoreTest {
assertFalse(iterator.hasNext());
}
}
+
+ /**
+ * Test creating a new shapefile.
+ */
+ @Ignore
+ @Test
+ public void testCreate() throws URISyntaxException, DataStoreException {
+ //todo
+ }
+
+ /**
+ * Test adding features to a shapefile.
+ */
+ @Ignore
+ @Test
+ public void testAddFeatures() throws URISyntaxException, DataStoreException {
+ //todo
+ }
+
+ /**
+ * Test remove features from a shapefile.
+ */
+ @Ignore
+ @Test
+ public void testRemoveFeatures() throws URISyntaxException, DataStoreException {
+ //todo
+ }
+
+ /**
+ * Test replacing features in a shapefile.
+ */
+ @Ignore
+ @Test
+ public void testReplaceFeatures() throws URISyntaxException, DataStoreException {
+ //todo
+ }
+
}
diff --git a/incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/dbf/DBFIOTest.java b/incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/dbf/DBFIOTest.java
index 45f3b5b132..b70f1e27cc 100644
--- a/incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/dbf/DBFIOTest.java
+++ b/incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/dbf/DBFIOTest.java
@@ -19,8 +19,6 @@ package org.apache.sis.storage.shapefile.dbf;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
-import java.nio.ByteBuffer;
-import java.nio.channels.WritableByteChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.OpenOption;
@@ -76,27 +74,27 @@ public class DBFIOTest {
assertEquals(78, header.fields[0].fieldType);
assertEquals(0, header.fields[0].fieldAddress);
assertEquals(10, header.fields[0].fieldLength);
- assertEquals(0, header.fields[0].fieldLDecimals);
+ assertEquals(0, header.fields[0].fieldDecimals);
assertEquals("text", header.fields[1].fieldName);
assertEquals(67, header.fields[1].fieldType);
assertEquals(0, header.fields[1].fieldAddress);
assertEquals(80, header.fields[1].fieldLength);
- assertEquals(0, header.fields[1].fieldLDecimals);
+ assertEquals(0, header.fields[1].fieldDecimals);
assertEquals("integer", header.fields[2].fieldName);
assertEquals(78, header.fields[2].fieldType);
assertEquals(0, header.fields[2].fieldAddress);
assertEquals(10, header.fields[2].fieldLength);
- assertEquals(0, header.fields[2].fieldLDecimals);
+ assertEquals(0, header.fields[2].fieldDecimals);
assertEquals("float", header.fields[3].fieldName);
assertEquals(78, header.fields[3].fieldType);
assertEquals(0, header.fields[3].fieldAddress);
assertEquals(11, header.fields[3].fieldLength);
- assertEquals(6, header.fields[3].fieldLDecimals);
+ assertEquals(6, header.fields[3].fieldDecimals);
assertEquals("date", header.fields[4].fieldName);
assertEquals(68, header.fields[4].fieldType);
assertEquals(0, header.fields[4].fieldAddress);
assertEquals(8, header.fields[4].fieldLength);
- assertEquals(0, header.fields[4].fieldLDecimals);
+ assertEquals(0, header.fields[4].fieldDecimals);
final DBFRecord record1 = reader.next();
@@ -108,7 +106,6 @@ public class DBFIOTest {
final DBFRecord record2 = reader.next();
assertEquals(2L, record2.fields[0]);
- assertEquals("text2", record2.fields[1]);
assertEquals(40L, record2.fields[2]);
assertEquals(60.0, record2.fields[3]);
assertEquals(LocalDate.of(2023, 10, 28), record2.fields[4]);