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 a798c10c97 feat(Shapefile): simplify raw API, reduce scope of all 
unnecessary attributes, full javadoc
a798c10c97 is described below

commit a798c10c972e96fbca6ad417c76979a28e2bf919
Author: jsorel <johann.so...@geomatys.com>
AuthorDate: Wed Dec 6 18:12:56 2023 +0100

    feat(Shapefile): simplify raw API, reduce scope of all unnecessary 
attributes, full javadoc
    
    SIS-188 : DBFField properties have been reduced
    
    SIS-188 : classes removed
    
    SIS-189 : exception removed
---
 .../main/module-info.java                          |   3 +-
 .../sis/storage/shapefile/ShapefileProvider.java   |  24 ++++
 .../sis/storage/shapefile/ShapefileStore.java      | 100 ++++++++++----
 .../{dbf/DBFRecord.java => cpg/package-info.java}  |  17 +--
 .../apache/sis/storage/shapefile/dbf/DBFField.java |  89 ++++++++++--
 .../sis/storage/shapefile/dbf/DBFHeader.java       |  63 +++++++--
 .../sis/storage/shapefile/dbf/DBFReader.java       |  48 +++++--
 .../sis/storage/shapefile/dbf/DBFWriter.java       |  39 ++++--
 .../dbf/{DBFRecord.java => package-info.java}      |  28 ++--
 .../apache/sis/storage/shapefile/package-info.java |  20 ++-
 .../shapefile/shp/ShapeGeometryEncoder.java        | 151 ++++++++++++++++-----
 .../sis/storage/shapefile/shp/ShapeHeader.java     |  15 +-
 .../sis/storage/shapefile/shp/ShapeReader.java     |  29 ++++
 .../sis/storage/shapefile/shp/ShapeRecord.java     |  55 ++++----
 .../sis/storage/shapefile/shp/ShapeType.java       |  83 +++++++----
 .../sis/storage/shapefile/shp/ShapeWriter.java     |  48 +++++--
 .../storage/shapefile/{ => shp}/package-info.java  |  13 +-
 .../sis/storage/shapefile/shx/IndexReader.java     |  27 +++-
 .../sis/storage/shapefile/shx/IndexWriter.java     |  37 +++--
 .../storage/shapefile/{ => shx}/package-info.java  |   9 +-
 .../org/apache/sis/storage/shapefile/Snippets.java | 106 +++++++++++++++
 .../sis/storage/shapefile/dbf/DBFIOTest.java       |  46 +++----
 .../apache/sis/storage/shapefile/dbf/Snippets.java |  95 +++++++++++++
 .../sis/storage/shapefile/shp/ShapeIOTest.java     |   4 +-
 .../apache/sis/storage/shapefile/shp/Snippets.java |  81 +++++++++++
 25 files changed, 979 insertions(+), 251 deletions(-)

diff --git 
a/incubator/src/org.apache.sis.storage.shapefile/main/module-info.java 
b/incubator/src/org.apache.sis.storage.shapefile/main/module-info.java
index e1b6d97f85..9bb7b321f2 100644
--- a/incubator/src/org.apache.sis.storage.shapefile/main/module-info.java
+++ b/incubator/src/org.apache.sis.storage.shapefile/main/module-info.java
@@ -26,8 +26,9 @@ module org.apache.sis.storage.shapefile {
 
     exports org.apache.sis.storage.shapefile;
     exports org.apache.sis.storage.shapefile.cpg;
-    exports org.apache.sis.storage.shapefile.shp;
     exports org.apache.sis.storage.shapefile.dbf;
+    exports org.apache.sis.storage.shapefile.shp;
+    exports org.apache.sis.storage.shapefile.shx;
 
     provides org.apache.sis.storage.DataStoreProvider
             with org.apache.sis.storage.shapefile.ShapefileProvider;
diff --git 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ShapefileProvider.java
 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ShapefileProvider.java
index 4a77845225..ecb18705b6 100644
--- 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ShapefileProvider.java
+++ 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ShapefileProvider.java
@@ -36,8 +36,14 @@ import org.apache.sis.storage.StorageConnector;
  */
 public final class ShapefileProvider extends DataStoreProvider {
 
+    /**
+     * Format name.
+     */
     public static final String NAME = "esri shapefile";
 
+    /**
+     * Format mime type.
+     */
     public static final String MIME_TYPE = "application/x-shapefile";
 
     /**
@@ -48,23 +54,38 @@ public final class ShapefileProvider extends 
DataStoreProvider {
             .setRequired(true)
             .create(URI.class, null);
 
+    /**
+     * Shapefile store creation parameters.
+     */
     public static final ParameterDescriptorGroup PARAMETERS_DESCRIPTOR =
             new 
ParameterBuilder().addName(NAME).addName("EsriShapefileParameters").createGroup(
                 PATH);
 
+    /**
+     * Default constructor.
+     */
     public ShapefileProvider() {
     }
 
+    /**
+     * {@inheritDoc }
+     */
     @Override
     public String getShortName() {
         return NAME;
     }
 
+    /**
+     * {@inheritDoc }
+     */
     @Override
     public ParameterDescriptorGroup getOpenParameters() {
         return PARAMETERS_DESCRIPTOR;
     }
 
+    /**
+     * {@inheritDoc }
+     */
     @Override
     public ProbeResult probeContent(StorageConnector connector) throws 
DataStoreException {
         final Path path = connector.getStorageAs(Path.class);
@@ -74,6 +95,9 @@ public final class ShapefileProvider extends 
DataStoreProvider {
         return ProbeResult.UNSUPPORTED_STORAGE;
     }
 
+    /**
+     * {@inheritDoc }
+     */
     @Override
     public DataStore open(StorageConnector connector) throws 
DataStoreException {
         final Path path = connector.getStorageAs(Path.class);
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 6c755a466e..9a77f574bc 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
@@ -99,7 +99,6 @@ import org.apache.sis.storage.shapefile.cpg.CpgFiles;
 import org.apache.sis.storage.shapefile.dbf.DBFField;
 import org.apache.sis.storage.shapefile.dbf.DBFHeader;
 import org.apache.sis.storage.shapefile.dbf.DBFReader;
-import org.apache.sis.storage.shapefile.dbf.DBFRecord;
 import org.apache.sis.storage.shapefile.dbf.DBFWriter;
 import org.apache.sis.storage.shapefile.shp.ShapeGeometryEncoder;
 import org.apache.sis.storage.shapefile.shp.ShapeHeader;
@@ -149,18 +148,33 @@ public final class ShapefileStore extends DataStore 
implements WritableFeatureSe
      */
     private final ReadWriteLock lock = new ReentrantReadWriteLock();
 
+    /**
+     * Construct store from given path.
+     *
+     * @param path path to .shp file
+     */
     public ShapefileStore(Path path) {
         this.shpPath = path;
         this.userDefinedCharSet = null;
         this.files = new ShpFiles(shpPath);
     }
 
+    /**
+     * Construct store from given connector.
+     *
+     * @param cnx not null
+     * @throws IllegalArgumentException if connector could not provide a valid 
Path instance
+     * @throws DataStoreException if connector could not provide a valid Path 
instance
+     */
     public ShapefileStore(StorageConnector cnx) throws 
IllegalArgumentException, DataStoreException {
         this.shpPath = cnx.getStorageAs(Path.class);
         this.userDefinedCharSet = cnx.getOption(OptionKey.ENCODING);
         this.files = new ShpFiles(shpPath);
     }
 
+    /**
+     * {@inheritDoc }
+     */
     @Override
     public Optional<ParameterValueGroup> getOpenParameters() {
         final Parameters parameters = 
Parameters.castOrWrap(ShapefileProvider.PARAMETERS_DESCRIPTOR.createValue());
@@ -168,63 +182,96 @@ public final class ShapefileStore extends DataStore 
implements WritableFeatureSe
         return Optional.of(parameters);
     }
 
+    /**
+     * {@inheritDoc }
+     */
     @Override
     public void close() throws DataStoreException {
     }
 
-    /*
-    Redirect FeatureSet interface to View
-    */
+    /**
+     * {@inheritDoc }
+     */
     @Override
     public Optional<GenericName> getIdentifier() throws DataStoreException {
         return featureSetView.getIdentifier();
     }
 
+    /**
+     * {@inheritDoc }
+     */
     @Override
     public Metadata getMetadata() throws DataStoreException {
         return featureSetView.getMetadata();
     }
 
+    /**
+     * {@inheritDoc }
+     */
     @Override
     public FeatureType getType() throws DataStoreException {
         return featureSetView.getType();
     }
 
+    /**
+     * {@inheritDoc }
+     */
     @Override
     public FeatureSet subset(Query query) throws UnsupportedQueryException, 
DataStoreException {
         return featureSetView.subset(query);
     }
 
+    /**
+     * {@inheritDoc }
+     */
     @Override
     public Stream<Feature> features(boolean parallel) throws 
DataStoreException {
         return featureSetView.features(parallel);
     }
 
+    /**
+     * {@inheritDoc }
+     */
     @Override
     public Optional<Envelope> getEnvelope() throws DataStoreException {
         return featureSetView.getEnvelope();
     }
 
+    /**
+     * {@inheritDoc }
+     */
     @Override
     public void updateType(FeatureType featureType) throws DataStoreException {
         featureSetView.updateType(featureType);
     }
 
+    /**
+     * {@inheritDoc }
+     */
     @Override
     public void add(Iterator<? extends Feature> iterator) throws 
DataStoreException {
         featureSetView.add(iterator);
     }
 
+    /**
+     * {@inheritDoc }
+     */
     @Override
     public void removeIf(Predicate<? super Feature> predicate) throws 
DataStoreException {
         featureSetView.removeIf(predicate);
     }
 
+    /**
+     * {@inheritDoc }
+     */
     @Override
     public void replaceIf(Predicate<? super Feature> predicate, 
UnaryOperator<Feature> unaryOperator) throws DataStoreException {
         featureSetView.replaceIf(predicate, unaryOperator);
     }
 
+    /**
+     * {@inheritDoc }
+     */
     @Override
     public Path[] getComponentFiles() throws DataStoreException {
         return featureSetView.getComponentFiles();
@@ -416,11 +463,11 @@ public final class ShapefileStore extends DataStore 
implements WritableFeatureSe
                             //move dbf to record offset, some shp record might 
have been skipped because of filter
                             long offset = (long)header.headerSize + 
((long)(shpRecord.recordNumber-1)) * ((long)header.recordSize);
                             dbfreader.moveToOffset(offset);
-                            final DBFRecord dbfRecord = dbfreader.next();
+                            final Object[] dbfRecord = dbfreader.next();
                             final Feature next = type.newInstance();
                             next.setPropertyValue(GEOMETRY_NAME, 
shpRecord.geometry);
                             for (int i = 0; i < dbfPropertiesIndex.length; 
i++) {
-                                
next.setPropertyValue(header.fields[dbfPropertiesIndex[i]].fieldName, 
dbfRecord.fields[i]);
+                                
next.setPropertyValue(header.fields[dbfPropertiesIndex[i]].fieldName, 
dbfRecord[i]);
                             }
                             action.accept(next);
                             return true;
@@ -453,11 +500,11 @@ public final class ShapefileStore extends DataStore 
implements WritableFeatureSe
                     @Override
                     public boolean tryAdvance(Consumer action) {
                         try {
-                            final DBFRecord dbfRecord = dbfreader.next();
+                            final Object[] dbfRecord = dbfreader.next();
                             if (dbfRecord == null) return false;
                             final Feature next = type.newInstance();
                             for (int i = 0; i < dbfPropertiesIndex.length; 
i++) {
-                                
next.setPropertyValue(header.fields[dbfPropertiesIndex[i]].fieldName, 
dbfRecord.fields[i]);
+                                
next.setPropertyValue(header.fields[dbfPropertiesIndex[i]].fieldName, 
dbfRecord[i]);
                             }
                             action.accept(next);
                             return true;
@@ -603,6 +650,7 @@ public final class ShapefileStore extends DataStore 
implements WritableFeatureSe
                 final ShapeHeader shpHeader = new ShapeHeader();
                 shpHeader.bbox = new ImmutableEnvelope(new GeneralEnvelope(4));
                 final DBFHeader dbfHeader = new DBFHeader();
+                dbfHeader.lastUpdate = LocalDate.now();
                 dbfHeader.fields = new DBFField[0];
                 final Charset charset = userDefinedCharSet == null ? 
StandardCharsets.UTF_8 : userDefinedCharSet;
                 CoordinateReferenceSystem crs = 
CommonCRS.WGS84.normalizedGeographic();
@@ -617,16 +665,16 @@ public final class ShapefileStore extends DataStore 
implements WritableFeatureSe
                         if (length == null || length == 0) length = 255;
 
                         if (Geometry.class.isAssignableFrom(valueClass)) {
-                            if (shpHeader.shapeType != 0) {
+                            if (shpHeader.shapeType != null) {
                                 throw new DataStoreException("Shapefile format 
can only contain one geometry");
                             }
-                            if (Point.class.isAssignableFrom(valueClass)) 
shpHeader.shapeType = ShapeType.VALUE_POINT;
+                            if (Point.class.isAssignableFrom(valueClass)) 
shpHeader.shapeType = ShapeType.POINT;
                             else if 
(MultiPoint.class.isAssignableFrom(valueClass))
-                                shpHeader.shapeType = 
ShapeType.VALUE_MULTIPOINT;
+                                shpHeader.shapeType = ShapeType.MULTIPOINT;
                             else if 
(LineString.class.isAssignableFrom(valueClass) || 
MultiLineString.class.isAssignableFrom(valueClass))
-                                shpHeader.shapeType = ShapeType.VALUE_POLYLINE;
+                                shpHeader.shapeType = ShapeType.POLYLINE;
                             else if 
(Polygon.class.isAssignableFrom(valueClass) || 
MultiPolygon.class.isAssignableFrom(valueClass))
-                                shpHeader.shapeType = ShapeType.VALUE_POLYGON;
+                                shpHeader.shapeType = ShapeType.POLYGON;
                             else throw new DataStoreException("Unsupported 
geometry type " + valueClass);
 
                             Object cdt = 
at.characteristics().get(AttributeConvention.CRS);
@@ -663,21 +711,21 @@ public final class ShapefileStore extends DataStore 
implements WritableFeatureSe
 
                 //write shapefile
                 try (ShapeWriter writer = new 
ShapeWriter(ShpFiles.openWriteChannel(files.shpFile, StandardOpenOption.CREATE, 
StandardOpenOption.TRUNCATE_EXISTING))) {
-                    writer.write(shpHeader);
+                    writer.writeHeader(shpHeader);
                 } catch (IOException ex) {
                     throw new DataStoreException("Failed to create shapefile 
(shp).", ex);
                 }
 
                 //write shx
                 try (IndexWriter writer = new 
IndexWriter(ShpFiles.openWriteChannel(files.getShx(true), 
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING))) {
-                    writer.write(shpHeader);
+                    writer.writeHeader(shpHeader);
                 } catch (IOException ex) {
                     throw new DataStoreException("Failed to create shapefile 
(shx).", ex);
                 }
 
                 //write dbf
                 try (DBFWriter writer = new 
DBFWriter(ShpFiles.openWriteChannel(files.getDbf(true), 
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING))) {
-                    writer.write(dbfHeader);
+                    writer.writeHeader(dbfHeader);
                 } catch (IOException ex) {
                     throw new DataStoreException("Failed to create shapefile 
(dbf).", ex);
                 }
@@ -1078,9 +1126,9 @@ public final class ShapefileStore extends DataStore 
implements WritableFeatureSe
                 shpWriter = new 
ShapeWriter(ShpFiles.openWriteChannel(tempFiles.shpFile, 
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING));
                 dbfWriter = new 
DBFWriter(ShpFiles.openWriteChannel(tempFiles.getDbf(true), 
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING));
                 shxWriter = new 
IndexWriter(ShpFiles.openWriteChannel(tempFiles.getShx(true), 
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING));
-                shpWriter.write(shpHeader);
-                shxWriter.write(shpHeader);
-                dbfWriter.write(dbfHeader);
+                shpWriter.writeHeader(shpHeader);
+                shxWriter.writeHeader(shpHeader);
+                dbfWriter.writeHeader(dbfHeader);
             } catch (IOException ex) {
                 try {
                     tempFiles.deleteFiles();
@@ -1095,7 +1143,6 @@ public final class ShapefileStore extends DataStore 
implements WritableFeatureSe
         private void write(Feature feature) throws IOException {
             inc++; //number starts at 1
             final ShapeRecord shpRecord = new ShapeRecord();
-            final DBFRecord dbfRecord = new DBFRecord();
             final long recordStartPosition = shpWriter.getSteamPosition();
 
             if (defaultGeomName == null) {
@@ -1122,18 +1169,18 @@ public final class ShapefileStore extends DataStore 
implements WritableFeatureSe
             } else {
                 throw new IOException("Feature geometry property is not a 
geometry");
             }
-            shpWriter.write(shpRecord);
+            shpWriter.writeRecord(shpRecord);
             final long recordEndPosition = shpWriter.getSteamPosition();
 
             //write index
-            shxWriter.write(Math.toIntExact(recordStartPosition), 
Math.toIntExact(recordEndPosition - recordStartPosition));
+            shxWriter.writeRecord(Math.toIntExact(recordStartPosition), 
Math.toIntExact(recordEndPosition - recordStartPosition));
 
             //copy dbf fields
-            dbfRecord.fields = new Object[dbfHeader.fields.length];
-            for (int i = 0; i < dbfRecord.fields.length; i++) {
-                dbfRecord.fields[i] = 
feature.getPropertyValue(dbfHeader.fields[i].fieldName);
+            Object[] fields = new Object[dbfHeader.fields.length];
+            for (int i = 0; i < fields.length; i++) {
+                fields[i] = 
feature.getPropertyValue(dbfHeader.fields[i].fieldName);
             }
-            dbfWriter.write(dbfRecord);
+            dbfWriter.writeRecord(fields);
         }
 
         /**
@@ -1160,5 +1207,4 @@ public final class ShapefileStore extends DataStore 
implements WritableFeatureSe
         }
     }
 
-
 }
diff --git 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/DBFRecord.java
 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/cpg/package-info.java
similarity index 73%
copy from 
incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/DBFRecord.java
copy to 
incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/cpg/package-info.java
index 1f33f949d7..93d9357898 100644
--- 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/DBFRecord.java
+++ 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/cpg/package-info.java
@@ -14,21 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sis.storage.shapefile.dbf;
-
 
 /**
- * A DBF record is an array of field values.
- *
- * @author Johann Sorel (Geomatys)
+ * Shapefile CPG files utilities.
  */
-public final class DBFRecord {
-
-    public static final DBFRecord DELETED = new DBFRecord();
-
-    public Object[] fields;
-
-    public DBFRecord() {
-    }
-
-}
+package org.apache.sis.storage.shapefile.cpg;
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 564bd7ba9d..341b4d4a86 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
@@ -27,6 +27,8 @@ import org.apache.sis.io.stream.ChannelDataOutput;
 
 
 /**
+ * Immutable DBase III field description.
+ * This class holds the different field attributes with value read and write 
capabilities.
  *
  * @author Johann Sorel (Geomatys)
  */
@@ -35,60 +37,83 @@ public final class DBFField {
     /**
      * Binary, String : b or B
      */
-    public static final int TYPE_BINARY = 'b';
+    public static final char TYPE_BINARY = 'b';
     /**
      * Characters : c or C
      */
-    public static final int TYPE_CHAR = 'c';
+    public static final char TYPE_CHAR = 'c';
     /**
      * Date : d or D
      */
-    public static final int TYPE_DATE = 'd';
+    public static final char TYPE_DATE = 'd';
     /**
      * Numeric : n or N
      */
-    public static final int TYPE_NUMBER = 'n';
+    public static final char TYPE_NUMBER = 'n';
     /**
      * Logical : l or L
      */
-    public static final int TYPE_LOGIC = 'l';
+    public static final char TYPE_LOGIC = 'l';
     /**
      * Memo, String : m or M
      */
-    public static final int TYPE_MEMO = 'm';
+    public static final char TYPE_MEMO = 'm';
     /**
      * TimeStamp : 8 bytes, two longs, first for date, second for time.
      * The date is the number of days since  01/01/4713 BC.
      * Time is hours * 3600000L + minutes * 60000L + Seconds * 1000L
      */
-    public static final int TYPE_TIMESTAMP = '@';
+    public static final char TYPE_TIMESTAMP = '@';
     /**
      * Long : i or I on 4 bytes, first bit is the sign, 0 = negative
      */
-    public static final int TYPE_LONG = 'i';
+    public static final char TYPE_LONG = 'i';
     /**
      * Autoincrement : same as Long
      */
-    public static final int TYPE_INC = '+';
+    public static final char TYPE_INC = '+';
     /**
      * Floats : f or F
      */
-    public static final int TYPE_FLOAT = 'f';
+    public static final char TYPE_FLOAT = 'f';
     /**
      * Double : o or O, real double on 8bytes, not string encoded
      */
-    public static final int TYPE_DOUBLE = 'o';
+    public static final char TYPE_DOUBLE = 'o';
     /**
      * OLE : g or G
      */
-    public static final int TYPE_OLE = 'g';
+    public static final char TYPE_OLE = 'g';
 
+    /**
+     * Field name.
+     */
     public final String fieldName;
+    /**
+     * Field type.
+     */
     public final char fieldType;
+    /**
+     * Reserved, found with different names in different spec.
+     * Unused in current implementation.
+     */
     public final int fieldAddress;
+    /**
+     * Field length in binary.
+     */
     public final int fieldLength;
+    /**
+     * Field decimal count in binary.
+     */
     public final int fieldDecimals;
-    public final Charset charset;
+    /**
+     * Used to decode strings.
+     * Can be null.
+     */
+    private final Charset charset;
+    /**
+     * Java value type matching field type.
+     */
     public final Class valueClass;
 
     private final ReadMethod reader;
@@ -96,6 +121,16 @@ public final class DBFField {
     //used by decimal format only;
     private NumberFormat format;
 
+    /**
+     * Field constructor.
+     *
+     * @param fieldName field name
+     * @param fieldType field data type
+     * @param fieldAddress unused for now, has a meaning in some 
specifications but not all
+     * @param fieldLength total field length in bytes
+     * @param fieldDecimals number of decimals for floating points
+     * @param charset String base field encoding
+     */
     public DBFField(String fieldName, char fieldType, int fieldAddress, int 
fieldLength, int fieldDecimals, Charset charset) {
         this.fieldName = fieldName;
         this.fieldType = fieldType;
@@ -131,6 +166,14 @@ public final class DBFField {
         }
     }
 
+    /**
+     * Read field description.
+     *
+     * @param channel to read from
+     * @param charset field text encoding
+     * @return dbf field description
+     * @throws IOException if an error occured while parsing field
+     */
     public static DBFField read(ChannelDataInput channel, Charset charset) 
throws IOException {
         byte[] n = channel.readBytes(11);
         int nameSize = 0;
@@ -144,6 +187,12 @@ public final class DBFField {
         return new DBFField(fieldName, fieldType, fieldAddress, fieldLength, 
fieldDecimals, charset);
     }
 
+    /**
+     * Write field description.
+     *
+     * @param channel to write into
+     * @throws IOException if an error occured while writing field
+     */
     public void write(ChannelDataOutput channel) throws IOException {
         byte[] bytes = fieldName.getBytes(StandardCharsets.US_ASCII);
         if (bytes.length > 11) throw new IOException("Field name length must 
not be longer then 11 characters.");
@@ -156,10 +205,24 @@ public final class DBFField {
         channel.repeat(14, (byte) 0);
     }
 
+    /**
+     * Read a value of this field type.
+     *
+     * @param channel to read from
+     * @return decoded field value
+     * @throws IOException if an error occured while parsing field value
+     */
     public Object readValue(ChannelDataInput channel) throws IOException {
         return reader.readValue(channel);
     }
 
+    /**
+     * Write a value of this field type.
+     *
+     * @param channel to write into
+     * @param value field value
+     * @throws IOException if an error occured while writing field value
+     */
     public void writeValue(ChannelDataOutput channel, Object value) throws 
IOException {
         writer.writeValue(channel, value);
     }
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 dde089f6d6..8c0bca0aa0 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
@@ -19,11 +19,13 @@ package org.apache.sis.storage.shapefile.dbf;
 import java.io.IOException;
 import java.nio.ByteOrder;
 import java.nio.charset.Charset;
+import java.time.LocalDate;
 import org.apache.sis.io.stream.ChannelDataInput;
 import org.apache.sis.io.stream.ChannelDataOutput;
 
 
 /**
+ * Mutable DBase III file header.
  *
  * @author Johann Sorel (Geomatys)
  */
@@ -32,21 +34,40 @@ public final class DBFHeader {
     private static final int FIELD_SIZE = 32;
     private static final int FIELD_DESCRIPTOR_TERMINATOR = 0x0D;
 
-    public int year;
-    public int month;
-    public int day;
+    /**
+     * Date of last update.
+     */
+    public LocalDate lastUpdate;
+    /**
+     * Number of records in the file.
+     */
     public int nbRecord;
+    /**
+     * Total header size.
+     */
     public int headerSize;
+    /**
+     * Size of a single record.
+     */
     public int recordSize;
+    /**
+     * Array of field descriptors.
+     */
     public DBFField[] fields;
 
+    /**
+     * Default constructor.
+     */
     public DBFHeader() {
     }
 
+    /**
+     * Duplicate constructor.
+     *
+     * @param toCopy to copy from
+     */
     public DBFHeader(DBFHeader toCopy) {
-        this.year = toCopy.year;
-        this.month = toCopy.month;
-        this.day = toCopy.day;
+        this.lastUpdate = toCopy.lastUpdate;
         this.nbRecord = toCopy.nbRecord;
         this.headerSize = toCopy.headerSize;
         this.recordSize = toCopy.recordSize;
@@ -65,14 +86,22 @@ public final class DBFHeader {
         }
     }
 
+    /**
+     * Read header.
+     *
+     * @param channel to read from
+     * @param charset field text encoding
+     * @throws IOException if an error occured while parsing header
+     */
     public void read(ChannelDataInput channel, Charset charset) throws 
IOException {
         channel.buffer.order(ByteOrder.LITTLE_ENDIAN);
         if (channel.readByte()!= 0x03) {
             throw new IOException("Unvalid database III magic");
         }
-        year       = channel.readUnsignedByte();
-        month      = channel.readUnsignedByte();
-        day        = channel.readUnsignedByte();
+        int year       = channel.readUnsignedByte();
+        int month      = channel.readUnsignedByte();
+        int day        = channel.readUnsignedByte();
+        lastUpdate = LocalDate.of(year+1900, month, day);
         nbRecord   = channel.readInt();
         headerSize = channel.readUnsignedShort();
         recordSize = channel.readUnsignedShort();
@@ -87,12 +116,18 @@ public final class DBFHeader {
         }
     }
 
+    /**
+     * Write header.
+     *
+     * @param channel to write into
+     * @throws IOException if an error occured while writing header
+     */
     public void write(ChannelDataOutput channel) throws IOException {
         channel.buffer.order(ByteOrder.LITTLE_ENDIAN);
         channel.writeByte(0x03);
-        channel.writeByte(year);
-        channel.writeByte(month);
-        channel.writeByte(day);
+        channel.writeByte(lastUpdate.getYear()-1900);
+        channel.writeByte(lastUpdate.getMonthValue());
+        channel.writeByte(lastUpdate.getDayOfMonth());
         channel.writeInt(nbRecord);
         channel.writeShort(headerSize);
         channel.writeShort(recordSize);
@@ -106,9 +141,7 @@ public final class DBFHeader {
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder("DBFHeader{");
-        sb.append("year=").append(year);
-        sb.append(",month=").append(month);
-        sb.append(",day=").append(day);
+        sb.append("lastUpdate=").append(lastUpdate);
         sb.append(",nbRecord=").append(nbRecord);
         sb.append(",headerSize=").append(headerSize);
         sb.append(",recordSize=").append(recordSize);
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 848743b5ac..40e7c97f0e 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
@@ -22,12 +22,17 @@ import org.apache.sis.io.stream.ChannelDataInput;
 
 
 /**
- * Seekable dbf file reader.
+ * Seekable DBase file reader.
  *
  * @author Johann Sorel (Geomatys)
  */
 public final class DBFReader implements AutoCloseable {
 
+    /**
+     * Unique instance used to mark a record which has been deleted.
+     */
+    public static final Object[] DELETED_RECORD = new Object[0];
+
     static final int TAG_PRESENT = 0x20;
     static final int TAG_DELETED = 0x2a;
     static final int TAG_EOF = 0x1A;
@@ -38,9 +43,12 @@ public final class DBFReader implements AutoCloseable {
     private int nbRead = 0;
 
     /**
+     * Constructor.
+     *
      * @param channel to read from
      * @param charset text encoding
      * @param fieldsToRead fields index in the header to decode, other fields 
will be skipped. must be in increment order.
+     * @throws IOException if a decoding error occurs on the header
      */
     public DBFReader(ChannelDataInput channel, Charset charset, int[] 
fieldsToRead) throws IOException {
         this.channel = channel;
@@ -49,24 +57,36 @@ public final class DBFReader implements AutoCloseable {
         this.fieldsToRead = fieldsToRead;
     }
 
+    /**
+     * Get decoded header.
+     *
+     * @return Dbase file header
+     */
     public DBFHeader getHeader() {
         return header;
     }
 
+    /**
+     * Move channel to given position.
+     *
+     * @param position new position
+     * @throws IOException if the stream cannot be moved to the given position.
+     */
     public void moveToOffset(long position) throws IOException {
         channel.seek(position);
     }
 
     /**
+     * Get next record.
      *
-     * @return record or DBFRecord.DELETED if this record has been deleted.
+     * @return record or DBFReader.DELETED_RECORD if this record has been 
deleted.
      * @throws IOException if a decoding error occurs
      */
-    public DBFRecord next() throws IOException {
+    public Object[] next() throws IOException {
         if (nbRead >= header.nbRecord) {
             //reached records end
             //we do not trust the EOF if we already have the expected count
-            //some writes do not have it
+            //some incorrect files do not have it
             return null;
         }
         nbRead++;
@@ -74,27 +94,26 @@ public final class DBFReader implements AutoCloseable {
         final int marker = channel.readUnsignedByte();
         if (marker == TAG_DELETED) {
             channel.seek(channel.getStreamPosition() + header.recordSize);
-            return DBFRecord.DELETED;
+            return DELETED_RECORD;
         } else if (marker == TAG_EOF) {
             return null;
         } else if (marker != TAG_PRESENT) {
             throw new IOException("Unexpected record marker " + marker);
         }
 
-        final DBFRecord record = new DBFRecord();
-        record.fields = new Object[header.fields.length];
+        Object[] record;
         if (fieldsToRead == null) {
             //read all fields
-            record.fields = new Object[header.fields.length];
+            record = new Object[header.fields.length];
             for (int i = 0; i < header.fields.length; i++) {
-                record.fields[i] = header.fields[i].readValue(channel);
+                record[i] = header.fields[i].readValue(channel);
             }
         } else {
             //read only selected fields
-            record.fields = new Object[fieldsToRead.length];
+            record = 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].readValue(channel);
+                    record[k++] = header.fields[i].readValue(channel);
                 } else {
                     //skip this field
                     channel.seek(channel.getStreamPosition() + 
header.fields[i].fieldLength);
@@ -105,8 +124,11 @@ public final class DBFReader implements AutoCloseable {
         return record;
     }
 
-
-
+    /**
+     * Release reader resources.
+     *
+     * @throws IOException If an I/O error occurs
+     */
     @Override
     public void close() throws IOException {
         channel.channel.close();
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 ec8acf06b3..ea7acbd53e 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
@@ -32,33 +32,54 @@ public final class DBFWriter implements AutoCloseable{
     private int writtenNbRecord = 0 ;
     private DBFHeader header;
 
+    /**
+     * Constructor.
+     *
+     * @param channel to write into
+     */
     public DBFWriter(ChannelDataOutput channel) {
         this.channel = channel;
     }
 
-    public void write(DBFHeader header) throws IOException {
+    /**
+     * Write DBase header.
+     *
+     * This should be the first call on the writer.
+     * Header number of records will be updated in the close method.
+     *
+     * @param header to write
+     * @throws IOException If an I/O error occurs
+     */
+    public void writeHeader(DBFHeader header) throws IOException {
         this.header = new DBFHeader(header);
         this.header.updateSizes(); //recompute sizes
         this.header.nbRecord = 0; //force to zero, will be replaced when 
closing writer.
         this.header.write(channel);
     }
 
-    public void write(DBFRecord record) throws IOException {
+    /**
+     * Write a DBase record.
+     *
+     * @param fieldValues record fields to write
+     * @throws IOException If an I/O error occurs
+     */
+    public void writeRecord(Object ... fieldValues) throws IOException {
         channel.writeByte(DBFReader.TAG_PRESENT);
         for (int i = 0; i < header.fields.length; i++) {
-            header.fields[i].writeValue(channel, record.fields[i]);
+            header.fields[i].writeValue(channel, fieldValues[i]);
         }
         writtenNbRecord++;
     }
 
-    public void flush() throws IOException {
-        channel.writeByte(DBFReader.TAG_EOF);
-        channel.flush();
-    }
-
+    /**
+     * Write end of file tag, update written number of record and release 
resources.
+     *
+     * @throws IOException If an I/O error occurs
+     */
     @Override
     public void close() throws IOException {
-        flush();
+        channel.writeByte(DBFReader.TAG_EOF);
+        channel.flush();
 
         //update the nbRecord in the header
         channel.seek(4);
diff --git 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/DBFRecord.java
 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/package-info.java
similarity index 56%
rename from 
incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/DBFRecord.java
rename to 
incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/package-info.java
index 1f33f949d7..3565b802da 100644
--- 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/DBFRecord.java
+++ 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/dbf/package-info.java
@@ -14,21 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sis.storage.shapefile.dbf;
-
 
 /**
- * A DBF record is an array of field values.
+ * DBase III format reader and writer.
+ * <p>
+ * This package is used and made for ESRI shapefile format which at it's 
creation time
+ * was related to DBase III.
+ *
+ * <h2>Reading example</h2>
+ *{@snippet class="org.apache.sis.storage.shapefile.dbf.Snippets" 
region="read"}
  *
- * @author Johann Sorel (Geomatys)
+ * <h2>Writing example</h2>
+ *{@snippet class="org.apache.sis.storage.shapefile.dbf.Snippets" 
region="write"}
+ *
+ * @see <a href="https://en.wikipedia.org/wiki/.dbf";>Format from Wikipedia</a>
+ * @see <a 
href="http://www.dbase.com/KnowledgeBase/int/db7_file_fmt.htm";>Format from 
dbase.com</a>
+ * @see <a href="https://wiki.dbfmanager.com/dbf-structure";>Format from 
wiki.dbfmanager.com</a>
  */
-public final class DBFRecord {
-
-    public static final DBFRecord DELETED = new DBFRecord();
-
-    public Object[] fields;
-
-    public DBFRecord() {
-    }
-
-}
+package org.apache.sis.storage.shapefile.dbf;
diff --git 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/package-info.java
 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/package-info.java
index 174e38140d..e2ac355205 100644
--- 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/package-info.java
+++ 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/package-info.java
@@ -16,11 +16,25 @@
  */
 
 /**
- * Shapefile.
+ * Shapefile format DataStore implementation.
  *
- * <div class="warning">This is an experimental package,
- * not yet target for any Apache SIS release at this time.</div>
+ * <h2>Reading example</h2>
+ *{@snippet class="org.apache.sis.storage.shapefile.Snippets" region="read"}
+ *
+ * <h2>Writing example</h2>
+ *{@snippet class="org.apache.sis.storage.shapefile.Snippets" region="write"}
+ *
+ * For raw access to DBF and SHP, use the related packages :
+ * <ul>
+ * <li>{@link org.apache.sis.storage.shapefile.shp}</li>
+ * <li>{@link org.apache.sis.storage.shapefile.shx}</li>
+ * <li>{@link org.apache.sis.storage.shapefile.dbf}</li>
+ * <li>{@link org.apache.sis.storage.shapefile.cpg}</li>
+ * </ul>
+ * The shapefile datastore layer is very thin and the only performance overheap
+ * is the mapping from DBFRecord/ShpRecord to Feature.
  *
  * @author Johann Sorel (Geomatys)
+ * @see <a 
href="http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf";>ESRI 
Shapefile Specification</a>
  */
 package org.apache.sis.storage.shapefile;
diff --git 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeGeometryEncoder.java
 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeGeometryEncoder.java
index c069f1bd39..98aef27229 100644
--- 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeGeometryEncoder.java
+++ 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeGeometryEncoder.java
@@ -24,12 +24,10 @@ import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import org.apache.sis.geometry.GeneralDirectPosition;
-import org.apache.sis.util.ArraysExt;
 import org.locationtech.jts.geom.*;
 import org.locationtech.jts.geom.impl.PackedCoordinateSequence;
 import org.locationtech.jts.algorithm.Orientation;
 import org.locationtech.jts.algorithm.RayCrossingCounter;
-import org.apache.sis.geometry.Envelope2D;
 import org.apache.sis.geometry.GeneralEnvelope;
 import org.apache.sis.io.stream.ChannelDataInput;
 import org.apache.sis.io.stream.ChannelDataOutput;
@@ -40,51 +38,71 @@ import org.apache.sis.io.stream.ChannelDataOutput;
  * This class should be kept separate because I might be used in ESRI 
geodatabase format.
  *
  * @author Johann Sorel (Geomatys)
+ * @param <T> encoder geometry type
  */
 public abstract class ShapeGeometryEncoder<T extends Geometry> {
 
     private static final GeometryFactory GF = new GeometryFactory();
 
-    protected final int shapeType;
+    /**
+     * Encoder shape type.
+     */
+    protected final ShapeType shapeType;
+    /**
+     * Encoder java value class.
+     */
     protected final Class<T> geometryClass;
+    /**
+     * Number of dimension in the geometry.
+     */
     protected final int dimension;
+    /**
+     * Number of measures in the geometry.
+     */
     protected final int measures;
+    /**
+     * Sum of dimension and measures.
+     */
     protected final int nbOrdinates;
 
     /**
+     * Get encoder for given shape type.
      *
      * @param shapeType shape type to encode
      * @return requested encoder
      */
-    public static ShapeGeometryEncoder getEncoder(int shapeType) {
+    public static ShapeGeometryEncoder getEncoder(ShapeType shapeType) {
         switch(shapeType) {
             //2D
-            case ShapeType.VALUE_NULL: return Null.INSTANCE;
-            case ShapeType.VALUE_POINT: return PointXY.INSTANCE;
-            case ShapeType.VALUE_POLYLINE: return Polyline.INSTANCE;
-            case ShapeType.VALUE_POLYGON: return Polygon.INSTANCE;
-            case ShapeType.VALUE_MULTIPOINT: return MultiPointXY.INSTANCE;
+            case NULL: return Null.INSTANCE;
+            case POINT: return PointXY.INSTANCE;
+            case POLYLINE: return Polyline.INSTANCE;
+            case POLYGON: return Polygon.INSTANCE;
+            case MULTIPOINT: return MultiPointXY.INSTANCE;
             //2D+1
-            case ShapeType.VALUE_POINT_M: return PointXYM.INSTANCE;
-            case ShapeType.VALUE_POLYLINE_M: return Polyline.INSTANCE_M;
-            case ShapeType.VALUE_POLYGON_M: return Polygon.INSTANCE_M;
-            case ShapeType.VALUE_MULTIPOINT_M: return MultiPointXYM.INSTANCE;
+            case POINT_M: return PointXYM.INSTANCE;
+            case POLYLINE_M: return Polyline.INSTANCE_M;
+            case POLYGON_M: return Polygon.INSTANCE_M;
+            case MULTIPOINT_M: return MultiPointXYM.INSTANCE;
             //3D+1
-            case ShapeType.VALUE_POINT_ZM: return PointXYZM.INSTANCE;
-            case ShapeType.VALUE_POLYLINE_ZM: return Polyline.INSTANCE_ZM;
-            case ShapeType.VALUE_POLYGON_ZM: return Polygon.INSTANCE_ZM;
-            case ShapeType.VALUE_MULTIPOINT_ZM: return MultiPointXYZM.INSTANCE;
-            case ShapeType.VALUE_MULTIPATCH_ZM: return MultiPatch.INSTANCE;
+            case POINT_ZM: return PointXYZM.INSTANCE;
+            case POLYLINE_ZM: return Polyline.INSTANCE_ZM;
+            case POLYGON_ZM: return Polygon.INSTANCE_ZM;
+            case MULTIPOINT_ZM: return MultiPointXYZM.INSTANCE;
+            case MULTIPATCH_ZM: return MultiPatch.INSTANCE;
             default: throw new IllegalArgumentException("unknown shape type");
         }
     }
 
     /**
+     * Constructor.
+     *
      * @param shapeType shape type code.
+     * @param geometryClass java geometry class
      * @param dimension number of dimensions in processed geometries.
      * @param measures number of measures in processed geometries.
      */
-    protected ShapeGeometryEncoder(int shapeType, Class<T> geometryClass, int 
dimension, int measures) {
+    protected ShapeGeometryEncoder(ShapeType shapeType, Class<T> 
geometryClass, int dimension, int measures) {
         this.shapeType = shapeType;
         this.geometryClass = geometryClass;
         this.dimension = dimension;
@@ -93,13 +111,17 @@ public abstract class ShapeGeometryEncoder<T extends 
Geometry> {
     }
 
     /**
-     * @return shape type code.
+     * Get shape type.
+     *
+     * @return shape type.
      */
-    public int getShapeType() {
+    public ShapeType getShapeType() {
         return shapeType;
     }
 
     /**
+     * Get java geometry value class.
+     *
      * @return geometry class handled by this encoder
      */
     public Class<T> getValueClass() {
@@ -107,6 +129,8 @@ public abstract class ShapeGeometryEncoder<T extends 
Geometry> {
     }
 
     /**
+     * Get number of dimensions in processed geometries.
+     *
      * @return number of dimensions in processed geometries.
      */
     public final int getDimension() {
@@ -114,6 +138,8 @@ public abstract class ShapeGeometryEncoder<T extends 
Geometry> {
     }
 
     /**
+     * Get number of measures in processed geometries.
+     *
      * @return number of measures in processed geometries.
      */
     public final int getMeasures() {
@@ -128,6 +154,7 @@ public abstract class ShapeGeometryEncoder<T extends 
Geometry> {
      * @param record to read into
      * @param filter optional filter envelope to stop geometry decoding as 
soon as possible
      * @return true if geometry pass the filter
+     * @throws IOException If an I/O error occurs
      */
     public abstract boolean decode(ChannelDataInput channel, ShapeRecord 
record, Rectangle2D.Double filter) throws IOException;
 
@@ -136,6 +163,7 @@ public abstract class ShapeGeometryEncoder<T extends 
Geometry> {
      *
      * @param channel to write into
      * @param shape geometry to encode
+     * @throws IOException If an I/O error occurs
      */
     public abstract void encode(ChannelDataOutput channel, ShapeRecord shape) 
throws IOException;
 
@@ -146,6 +174,12 @@ public abstract class ShapeGeometryEncoder<T extends 
Geometry> {
      */
     public abstract int getEncodedLength(Geometry geom);
 
+    /**
+     * Calculate geometry bounding box.
+     *
+     * @param geom to compute
+     * @return geometry bounding box
+     */
     public abstract GeneralEnvelope getBoundingBox(Geometry geom);
 
     /**
@@ -155,6 +189,7 @@ public abstract class ShapeGeometryEncoder<T extends 
Geometry> {
      * @param shape to write into
      * @param filter optional filter envelope to stop geometry decoding as 
soon as possible
      * @return true if filter match or is null
+     * @throws IOException If an I/O error occurs
      */
     protected boolean readBBox2D(ChannelDataInput channel, ShapeRecord shape, 
Rectangle2D.Double filter) throws IOException {
         final double minX = channel.readDouble();
@@ -178,6 +213,7 @@ public abstract class ShapeGeometryEncoder<T extends 
Geometry> {
      *
      * @param channel to write into
      * @param shape to read from
+     * @throws IOException If an I/O error occurs
      */
     protected void writeBBox2D(ChannelDataOutput channel, ShapeRecord shape) 
throws IOException {
         final Envelope env2d = shape.geometry.getEnvelopeInternal();
@@ -188,11 +224,14 @@ public abstract class ShapeGeometryEncoder<T extends 
Geometry> {
     }
 
     /**
+     * Read encoded lines.
+     *
      * @param channel to read from
      * @param shape to write into
      * @param filter optional filter envelope to stop geometry decoding as 
soon as possible
      * @param asRing true to produce LinearRing instead of LineString
      * @return null if filter do no match
+     * @throws IOException If an I/O error occurs
      */
     protected LineString[] readLines(ChannelDataInput channel, ShapeRecord 
shape, Rectangle2D.Double filter, boolean asRing) throws IOException {
         if (!readBBox2D(channel, shape, filter)) return null;
@@ -234,6 +273,15 @@ public abstract class ShapeGeometryEncoder<T extends 
Geometry> {
         return lines;
     }
 
+    /**
+     * Read lines ordinates.
+     *
+     * @param channel to read from
+     * @param shape to update
+     * @param lines to update
+     * @param ordinateIndex ordinate index to read
+     * @throws IOException If an I/O error occurs
+     */
     protected void readLineOrdinates(ChannelDataInput channel, ShapeRecord 
shape, LineString[] lines, int ordinateIndex) throws IOException {
         final int nbDim = getDimension() + getMeasures();
         shape.bbox.setRange(ordinateIndex, channel.readDouble(), 
channel.readDouble());
@@ -246,6 +294,13 @@ public abstract class ShapeGeometryEncoder<T extends 
Geometry> {
         }
     }
 
+    /**
+     * Write given lines.
+     *
+     * @param channel to write into
+     * @param shape to write
+     * @throws IOException if an I/O exception occurs
+     */
     protected void writeLines(ChannelDataOutput channel, ShapeRecord shape) 
throws IOException {
         writeBBox2D(channel, shape);
         final List<LineString> lines = extractRings(shape.geometry);
@@ -277,6 +332,16 @@ public abstract class ShapeGeometryEncoder<T extends 
Geometry> {
         if (nbOrdinates == 4)  writeLineOrdinates(channel, shape, lines, 3, 
nbPts);
     }
 
+    /**
+     * Write given lines ordinates.
+     *
+     * @param channel to write into
+     * @param shape to write
+     * @param lines to write
+     * @param ordinateIndex line coordinate ordinate to write
+     * @param nbPts number of points
+     * @throws IOException if an I/O exception occurs
+     */
     protected void writeLineOrdinates(ChannelDataOutput channel, ShapeRecord 
shape,List<LineString> lines, int ordinateIndex, int nbPts) throws IOException {
 
         final double[] values = new double[nbPts];
@@ -297,6 +362,12 @@ public abstract class ShapeGeometryEncoder<T extends 
Geometry> {
         channel.writeDoubles(values);
     }
 
+    /**
+     * Extract all linear elements of given geometry.
+     *
+     * @param geom to extract lines from.
+     * @return list of lines
+     */
     protected List<LineString> extractRings(Geometry geom) {
         final List<LineString> lst = new ArrayList();
         extractRings(geom, lst);
@@ -384,6 +455,12 @@ public abstract class ShapeGeometryEncoder<T extends 
Geometry> {
         }
     }
 
+    /**
+     * Compute geometry bounding box.
+     *
+     * @param geom to compute
+     * @return geometry bounding box
+     */
     protected GeneralEnvelope getLinesBoundingBox(Geometry geom) {
         final List<LineString> lines = extractRings(geom);
 
@@ -434,7 +511,7 @@ public abstract class ShapeGeometryEncoder<T extends 
Geometry> {
         private static final Null INSTANCE = new Null();
 
         private Null() {
-            super(ShapeType.VALUE_NULL, Geometry.class, 2, 0);
+            super(ShapeType.NULL, Geometry.class, 2, 0);
         }
 
         @Override
@@ -462,7 +539,7 @@ public abstract class ShapeGeometryEncoder<T extends 
Geometry> {
         private static final PointXY INSTANCE = new PointXY();
 
         private PointXY() {
-            super(ShapeType.VALUE_POINT, Point.class, 2,0);
+            super(ShapeType.POINT, Point.class, 2,0);
         }
 
         @Override
@@ -504,7 +581,7 @@ public abstract class ShapeGeometryEncoder<T extends 
Geometry> {
 
         private static final PointXYM INSTANCE = new PointXYM();
         private PointXYM() {
-            super(ShapeType.VALUE_POINT_M, Point.class, 2, 1);
+            super(ShapeType.POINT_M, Point.class, 2, 1);
         }
 
         @Override
@@ -552,7 +629,7 @@ public abstract class ShapeGeometryEncoder<T extends 
Geometry> {
         private static final PointXYZM INSTANCE = new PointXYZM();
 
         private PointXYZM() {
-            super(ShapeType.VALUE_POINT_ZM, Point.class, 3, 1);
+            super(ShapeType.POINT_ZM, Point.class, 3, 1);
         }
 
         @Override
@@ -598,7 +675,7 @@ public abstract class ShapeGeometryEncoder<T extends 
Geometry> {
 
         private static final MultiPointXY INSTANCE = new MultiPointXY();
         private MultiPointXY() {
-            super(ShapeType.VALUE_MULTIPOINT, MultiPoint.class, 2, 0);
+            super(ShapeType.MULTIPOINT, MultiPoint.class, 2, 0);
         }
 
         @Override
@@ -650,7 +727,7 @@ public abstract class ShapeGeometryEncoder<T extends 
Geometry> {
         private static final MultiPointXYM INSTANCE = new MultiPointXYM();
 
         private MultiPointXYM() {
-            super(ShapeType.VALUE_MULTIPOINT_M, MultiPoint.class, 2, 1);
+            super(ShapeType.MULTIPOINT_M, MultiPoint.class, 2, 1);
         }
 
         @Override
@@ -722,7 +799,7 @@ public abstract class ShapeGeometryEncoder<T extends 
Geometry> {
         private static final MultiPointXYZM INSTANCE = new MultiPointXYZM();
 
         private MultiPointXYZM() {
-            super(ShapeType.VALUE_MULTIPOINT_ZM, MultiPoint.class, 3, 1);
+            super(ShapeType.MULTIPOINT_ZM, MultiPoint.class, 3, 1);
         }
 
         @Override
@@ -810,11 +887,11 @@ public abstract class ShapeGeometryEncoder<T extends 
Geometry> {
 
     private static class Polyline extends 
ShapeGeometryEncoder<MultiLineString> {
 
-        private static final Polyline INSTANCE = new 
Polyline(ShapeType.VALUE_POLYLINE, 2, 0);
-        private static final Polyline INSTANCE_M = new 
Polyline(ShapeType.VALUE_POLYLINE_M, 3, 0);
-        private static final Polyline INSTANCE_ZM = new 
Polyline(ShapeType.VALUE_POLYLINE_ZM, 3, 1);
+        private static final Polyline INSTANCE = new 
Polyline(ShapeType.POLYLINE, 2, 0);
+        private static final Polyline INSTANCE_M = new 
Polyline(ShapeType.POLYLINE_M, 3, 0);
+        private static final Polyline INSTANCE_ZM = new 
Polyline(ShapeType.POLYLINE_ZM, 3, 1);
 
-        private Polyline(int shapeType, int dimension, int measures) {
+        private Polyline(ShapeType shapeType, int dimension, int measures) {
             super(shapeType, MultiLineString.class, dimension, measures);
         }
 
@@ -850,11 +927,11 @@ public abstract class ShapeGeometryEncoder<T extends 
Geometry> {
 
     private static class Polygon extends ShapeGeometryEncoder<MultiPolygon> {
 
-        private static final Polygon INSTANCE = new 
Polygon(ShapeType.VALUE_POLYGON, 2, 0);
-        private static final Polygon INSTANCE_M = new 
Polygon(ShapeType.VALUE_POLYGON_M, 3, 0);
-        private static final Polygon INSTANCE_ZM = new 
Polygon(ShapeType.VALUE_POLYGON_ZM, 3, 1);
+        private static final Polygon INSTANCE = new Polygon(ShapeType.POLYGON, 
2, 0);
+        private static final Polygon INSTANCE_M = new 
Polygon(ShapeType.POLYGON_M, 3, 0);
+        private static final Polygon INSTANCE_ZM = new 
Polygon(ShapeType.POLYGON_ZM, 3, 1);
 
-        private Polygon(int shapeType, int dimension, int measures) {
+        private Polygon(ShapeType shapeType, int dimension, int measures) {
             super(shapeType, MultiPolygon.class, dimension, measures);
         }
 
@@ -897,7 +974,7 @@ public abstract class ShapeGeometryEncoder<T extends 
Geometry> {
         private static final MultiPatch INSTANCE = new MultiPatch();
 
         private MultiPatch() {
-            super(ShapeType.VALUE_MULTIPATCH_ZM, MultiPolygon.class, 3, 1);
+            super(ShapeType.MULTIPATCH_ZM, MultiPolygon.class, 3, 1);
         }
 
         @Override
diff --git 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeHeader.java
 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeHeader.java
index dd89bdd7c8..1230638311 100644
--- 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeHeader.java
+++ 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeHeader.java
@@ -48,17 +48,24 @@ public final class ShapeHeader {
     /**
      * Shape type.
      */
-    public int shapeType;
+    public ShapeType shapeType;
     /**
      * Shapefile bounding box without CRS.
      * Ordinates are in X,Y,Z,M order.
      */
     public ImmutableEnvelope bbox;
 
+    /**
+     * Default constructor
+     */
     public ShapeHeader() {
-
     }
 
+    /**
+     * Copy constructor.
+     *
+     * @param toCopy header to copy
+     */
     public ShapeHeader(ShapeHeader toCopy) {
         this.fileLength = toCopy.fileLength;
         this.shapeType = toCopy.shapeType;
@@ -85,7 +92,7 @@ public final class ShapeHeader {
         if (version != 1000) {
             throw new IOException("Incorrect file version, expected 1000 but 
was " + version);
         }
-        shapeType = channel.readInt();
+        shapeType = ShapeType.get(channel.readInt());
         final double[] bb = channel.readDoubles(8);
         GeneralEnvelope bbox = new GeneralEnvelope(4);
         bbox.setRange(0, bb[0], bb[2]);
@@ -107,7 +114,7 @@ public final class ShapeHeader {
         channel.writeInt(fileLength/2);
         channel.buffer.order(ByteOrder.LITTLE_ENDIAN);
         channel.writeInt(1000);
-        channel.writeInt(shapeType);
+        channel.writeInt(shapeType.getCode());
         channel.writeDouble(bbox.getMinimum(0));
         channel.writeDouble(bbox.getMinimum(1));
         channel.writeDouble(bbox.getMaximum(0));
diff --git 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeReader.java
 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeReader.java
index b1e097dfad..2564bd4e1f 100644
--- 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeReader.java
+++ 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeReader.java
@@ -36,6 +36,13 @@ public final class ShapeReader implements AutoCloseable{
     private final ShapeGeometryEncoder geomParser;
     private final Rectangle2D.Double filter;
 
+    /**
+     * Construct reader from given channel and an optional rectangle filter.
+     *
+     * @param channel to read from
+     * @param filter optional filtering rectangle
+     * @throws IOException if a decoding error occurs
+     */
     public ShapeReader(ChannelDataInput channel, Rectangle2D.Double filter) 
throws IOException {
         Objects.nonNull(channel);
         this.channel = channel;
@@ -45,14 +52,31 @@ public final class ShapeReader implements AutoCloseable{
         geomParser = ShapeGeometryEncoder.getEncoder(header.shapeType);
     }
 
+    /**
+     * Get header.
+     *
+     * @return shapefile header
+     */
     public ShapeHeader getHeader() {
         return header;
     }
 
+    /**
+     * Move channel to given position.
+     *
+     * @param position new position
+     * @throws IOException if the stream cannot be moved to the given position.
+     */
     public void moveToOffset(long position) throws IOException {
         channel.seek(position);
     }
 
+    /**
+     * Get next record.
+     *
+     * @return record or null if there is no more record
+     * @throws IOException if a decoding error occurs
+     */
     public ShapeRecord next() throws IOException {
         final ShapeRecord record = new ShapeRecord();
         try {
@@ -72,6 +96,11 @@ public final class ShapeReader implements AutoCloseable{
         }
     }
 
+    /**
+     * Release reader resources.
+     *
+     * @throws IOException If an I/O error occurs
+     */
     @Override
     public void close() throws IOException {
         channel.channel.close();
diff --git 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeRecord.java
 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeRecord.java
index 77a0776864..f288e045f4 100644
--- 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeRecord.java
+++ 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeRecord.java
@@ -19,18 +19,15 @@ package org.apache.sis.storage.shapefile.shp;
 import java.awt.geom.Rectangle2D;
 import java.io.IOException;
 import java.nio.ByteOrder;
-
-import org.locationtech.jts.geom.Envelope;
 import org.locationtech.jts.geom.Geometry;
 import org.apache.sis.geometry.GeneralEnvelope;
-import org.apache.sis.geometry.Envelope2D;
 import org.apache.sis.io.stream.ChannelDataInput;
 import org.apache.sis.io.stream.ChannelDataOutput;
-import org.locationtech.jts.geom.MultiPoint;
-import org.locationtech.jts.geom.Point;
-
 
 /**
+ * A record in a shape file.
+ * Contains a unique record number and it's associated geometry.
+ *
  * @author Johann Sorel (Geomatys)
  */
 public final class ShapeRecord {
@@ -40,19 +37,36 @@ public final class ShapeRecord {
      */
     public int recordNumber;
     /**
-     * Encoded geometry.
+     * Record geometry
      */
-    public byte[] content;
-
     public Geometry geometry;
-
+    /**
+     * Geometry bounding box
+     */
     public GeneralEnvelope bbox;
 
+    /**
+     * Default constructor
+     */
+    public ShapeRecord() {
+    }
+
+    /**
+     * Constructor with initialization.
+     *
+     * @param recordNumber initial record number
+     * @param geometry initial geometry
+     */
+    public ShapeRecord(int recordNumber, Geometry geometry) {
+        this.recordNumber = recordNumber;
+        this.geometry = geometry;
+    }
+
     /**
      * Read this shape record.
      *
      * @param channel input channel, not null
-     * @param io geometry decoder, if null gemetry content will be stored in 
content array, otherwise geometry will be parsed
+     * @param io geometry decoder
      * @param filter optional filter envelope to stop geometry decoding as 
soon as possible
      * @return true if geometry pass the filter or if there is no filter
      * @throws IOException if an error occurred while reading.
@@ -66,31 +80,26 @@ public final class ShapeRecord {
         final long position = channel.getStreamPosition();
         channel.buffer.order(ByteOrder.LITTLE_ENDIAN);
         final int shapeType = channel.readInt();
-        if (io == null) {
-            content = channel.readBytes(byteSize);
-            return true;
-        } else {
-            final boolean match = io.decode(channel,this, filter);
-            if (!match) {
-                //move to record end
-                channel.seek(position + byteSize);
-            }
-            return match;
+        final boolean match = io.decode(channel,this, filter);
+        if (!match) {
+            //move to record end
+            channel.seek(position + byteSize);
         }
+        return match;
     }
 
     /**
      * Write this shape record.
      * @param channel output channel to write into, not null
      * @param io geometry encoder
-     * @throws IOException
+     * @throws IOException if an error occurred while writing.
      */
     public void write(ChannelDataOutput channel, ShapeGeometryEncoder io) 
throws IOException {
         channel.buffer.order(ByteOrder.BIG_ENDIAN);
         channel.writeInt(recordNumber);
         channel.writeInt((io.getEncodedLength(geometry) + 4) / 2); // +4 for 
shape type and /2 because size is in 16bit words
         channel.buffer.order(ByteOrder.LITTLE_ENDIAN);
-        channel.writeInt(io.getShapeType());
+        channel.writeInt(io.getShapeType().getCode());
         io.encode(channel, this);
     }
 }
diff --git 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeType.java
 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeType.java
index 08b0d452b7..2eb9a32b56 100644
--- 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeType.java
+++ 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeType.java
@@ -35,56 +35,93 @@ import java.util.Map;
  */
 public enum ShapeType {
 
+    /**
+     * Null geometry type
+     */
     NULL (0),
+    /**
+     * Point geometry type
+     */
     POINT(1),
+    /**
+     * Polyline geometry type
+     */
     POLYLINE(3),
+    /**
+     * Polygon geometry type
+     */
     POLYGON(5),
+    /**
+     * MultiPoint geometry type
+     */
     MULTIPOINT(8),
+    /**
+     * Point with M geometry type
+     */
     POINT_M(11),
+    /**
+     * Polyline with M geometry type
+     */
     POLYLINE_M(13),
+    /**
+     * Polygon with M geometry type
+     */
     POLYGON_M(15),
+    /**
+     * MultiPoint with M geometry type
+     */
     MULTIPOINT_M(18),
+    /**
+     * Point with Z and M geometry type
+     */
     POINT_ZM(21),
+    /**
+     * Polyline with Z and M geometry type
+     */
     POLYLINE_ZM(23),
+    /**
+     * Polygon with Z and M geometry type
+     */
     POLYGON_ZM(25),
+    /**
+     * MultiPoint with Z and M geometry type
+     */
     MULTIPOINT_ZM(28),
+    /**
+     * MultiPatch with Z and M geometry type
+     */
     MULTIPATCH_ZM(31);
 
-    public static final int VALUE_NULL = 0;
-    public static final int VALUE_POINT = 1;
-    public static final int VALUE_POLYLINE = 3;
-    public static final int VALUE_POLYGON = 5;
-    public static final int VALUE_MULTIPOINT = 8;
-    public static final int VALUE_POINT_M = 11;
-    public static final int VALUE_POLYLINE_M = 13;
-    public static final int VALUE_POLYGON_M = 15;
-    public static final int VALUE_MULTIPOINT_M = 18;
-    public static final int VALUE_POINT_ZM = 21;
-    public static final int VALUE_POLYLINE_ZM = 23;
-    public static final int VALUE_POLYGON_ZM = 25;
-    public static final int VALUE_MULTIPOINT_ZM = 28;
-    public static final int VALUE_MULTIPATCH_ZM = 31;
-
-    // used for initializing the enumeration
-    public final int value;
+    private final int code;
 
     private ShapeType (int value ) {
-        this.value = value;
+        this.code = value;
     }
 
-    public int getValue() {
-        return value;
+    /**
+     * Get geometry type code.
+     *
+     * @return geometry type code
+     */
+    public int getCode() {
+        return code;
     }
 
     private static final Map<Integer, ShapeType> lookup = new HashMap<Integer, 
ShapeType>();
 
     static {
         for (ShapeType ste : EnumSet.allOf(ShapeType.class)) {
-            lookup.put(ste.getValue(), ste);
+            lookup.put(ste.getCode(), ste);
         }
     }
 
-    public static ShapeType get(int value) {
-        return lookup.get(value);
+    /**
+     * Get geometry type for given code.
+     *
+     * @param code geometry code
+     * @return ShapeType for given code
+     */
+    public static ShapeType get(int code) {
+        return lookup.get(code);
     }
 }
diff --git 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeWriter.java
 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeWriter.java
index cabd09d663..ccce608141 100644
--- 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeWriter.java
+++ 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeWriter.java
@@ -20,6 +20,7 @@ import java.io.IOException;
 import org.apache.sis.io.stream.ChannelDataOutput;
 import org.apache.sis.geometry.GeneralEnvelope;
 import org.apache.sis.geometry.ImmutableEnvelope;
+import org.locationtech.jts.geom.Geometry;
 
 
 /**
@@ -35,15 +36,26 @@ public final class ShapeWriter implements AutoCloseable{
     private GeneralEnvelope bbox;
     private ShapeGeometryEncoder io;
 
-    public ShapeWriter(ChannelDataOutput channel) throws IOException {
+    /**
+     * Construct writer above given channel.
+     *
+     * @param channel to write into
+     */
+    public ShapeWriter(ChannelDataOutput channel) {
         this.channel = channel;
     }
 
+    /**
+     * Get header, not null after writeHeader has been call.
+     * @return shapefile header
+     */
     public ShapeHeader getHeader() {
         return header;
     }
 
     /**
+     * Get current position in the stream.
+     *
      * @return current position in the stream
      */
     public long getSteamPosition() {
@@ -53,8 +65,10 @@ public final class ShapeWriter implements AutoCloseable{
     /**
      * Header will be copied and modified.
      * Use getHeader to obtain the new header.
+     * @param header to write, not null
+     * @throws IOException If an I/O error occurs
      */
-    public void write(ShapeHeader header) throws IOException {
+    public void writeHeader(ShapeHeader header) throws IOException {
         this.header = new ShapeHeader(header);
         this.header.fileLength = 0;
         this.header.bbox = new ImmutableEnvelope(new GeneralEnvelope(4));
@@ -62,7 +76,24 @@ public final class ShapeWriter implements AutoCloseable{
         io = ShapeGeometryEncoder.getEncoder(header.shapeType);
     }
 
-    public void write(ShapeRecord record) throws IOException {
+    /**
+     * Write a new record.
+     *
+     * @param recordNumber record number
+     * @param geometry record geometry
+     * @throws IOException If an I/O error occurs
+     */
+    public void writeRecord(int recordNumber, Geometry geometry) throws 
IOException {
+        writeRecord(new ShapeRecord(recordNumber, geometry));
+    }
+
+    /**
+     * Write a new record.
+     *
+     * @param record new record
+     * @throws IOException If an I/O error occurs
+     */
+    public void writeRecord(ShapeRecord record) throws IOException {
         record.write(channel, io);
         final GeneralEnvelope geomBox = io.getBoundingBox(record.geometry);
         if (bbox == null) {
@@ -73,13 +104,14 @@ public final class ShapeWriter implements AutoCloseable{
         }
     }
 
-    public void flush() throws IOException {
-        channel.flush();
-    }
-
+    /**
+     * Release writer resources.
+     *
+     * @throws IOException If an I/O error occurs
+     */
     @Override
     public void close() throws IOException {
-        flush();
+        channel.flush();
 
         //update header and rewrite it
         //update the file length
diff --git 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/package-info.java
 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/package-info.java
similarity index 66%
copy from 
incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/package-info.java
copy to 
incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/package-info.java
index 174e38140d..e5c4125d38 100644
--- 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/package-info.java
+++ 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/package-info.java
@@ -16,11 +16,14 @@
  */
 
 /**
- * Shapefile.
+ * Shapefile format reader and writer.
  *
- * <div class="warning">This is an experimental package,
- * not yet target for any Apache SIS release at this time.</div>
+ * <h2>Reading example</h2>
+ *{@snippet class="org.apache.sis.storage.shapefile.shp.Snippets" 
region="read"}
  *
- * @author Johann Sorel (Geomatys)
+ * <h2>Writing example</h2>
+ *{@snippet class="org.apache.sis.storage.shapefile.shp.Snippets" 
region="write"}
+ *
+ * @see <a 
href="http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf";>ESRI 
Shapefile Specification</a>
  */
-package org.apache.sis.storage.shapefile;
+package org.apache.sis.storage.shapefile.shp;
diff --git 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shx/IndexReader.java
 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shx/IndexReader.java
index ad0d49d525..c0cdcd40c8 100644
--- 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shx/IndexReader.java
+++ 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shx/IndexReader.java
@@ -32,22 +32,42 @@ public final class IndexReader implements AutoCloseable{
     private final ChannelDataInput channel;
     private final ShapeHeader header;
 
+    /**
+     * Constructor.
+     *
+     * @param channel to read from
+     * @throws IOException if a decoding error occurs on the header
+     */
     public IndexReader(ChannelDataInput channel) throws IOException {
         this.channel = channel;
         header = new ShapeHeader();
         header.read(channel);
     }
 
+    /**
+     * Get shapefile header.
+     *
+     * @return shapefile header
+     */
     public ShapeHeader getHeader() {
         return header;
     }
 
+    /**
+     * Move channel to given position.
+     *
+     * @param position new position
+     * @throws IOException if the stream cannot be moved to the given position.
+     */
     public void moveToOffset(long position) throws IOException {
         channel.seek(position);
     }
 
     /**
-     * @return offset and length of the record in the shp file
+     * Get next record.
+     *
+     * @return next offset and length of the record in the shp file, null if 
no more records
+     * @throws IOException if a decoding error occurs
      */
     public int[] next() throws IOException {
         try {
@@ -58,6 +78,11 @@ public final class IndexReader implements AutoCloseable{
         }
     }
 
+    /**
+     * Release reader resources.
+     *
+     * @throws IOException If an I/O error occurs
+     */
     @Override
     public void close() throws IOException {
         channel.channel.close();
diff --git 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shx/IndexWriter.java
 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shx/IndexWriter.java
index b8666de7d0..2a242521aa 100644
--- 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shx/IndexWriter.java
+++ 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shx/IndexWriter.java
@@ -32,35 +32,56 @@ public final class IndexWriter implements AutoCloseable{
 
     private ShapeHeader header;
 
-    public IndexWriter(ChannelDataOutput channel) throws IOException {
+    /**
+     * Constructor.
+     *
+     * @param channel to write into
+     */
+    public IndexWriter(ChannelDataOutput channel) {
         this.channel = channel;
     }
 
+    /**
+     * Get shapefile header, null before the call to writeHeader.
+     *
+     * @return shapefile header
+     */
     public ShapeHeader getHeader() {
         return header;
     }
     /**
      * Header will be copied and modified.
      * Use getHeader to obtain the new header.
+     *
+     * @param header to write
+     * @throws IOException If an I/O error occurs
      */
-    public void write(ShapeHeader header) throws IOException {
+    public void writeHeader(ShapeHeader header) throws IOException {
         this.header = new ShapeHeader(header);
         this.header.fileLength = 0;
         header.write(channel);
     }
 
-    public void write(int offset, int length) throws IOException {
+    /**
+     * Write a new record.
+     *
+     * @param offset record offset
+     * @param length record length
+     * @throws IOException If an I/O error occurs
+     */
+    public void writeRecord(int offset, int length) throws IOException {
         channel.writeInt(offset);
         channel.writeInt(length);
     }
 
-    public void flush() throws IOException {
-        channel.flush();
-    }
-
+    /**
+     * Release writer resources.
+     *
+     * @throws IOException If an I/O error occurs
+     */
     @Override
     public void close() throws IOException {
-        flush();
+        channel.flush();
 
         //update header and rewrite it
         //update the file length
diff --git 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/package-info.java
 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shx/package-info.java
similarity index 78%
copy from 
incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/package-info.java
copy to 
incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shx/package-info.java
index 174e38140d..e8a06a540e 100644
--- 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/package-info.java
+++ 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shx/package-info.java
@@ -16,11 +16,6 @@
  */
 
 /**
- * Shapefile.
- *
- * <div class="warning">This is an experimental package,
- * not yet target for any Apache SIS release at this time.</div>
- *
- * @author Johann Sorel (Geomatys)
+ * Shapefile Shx index reader and writer.
  */
-package org.apache.sis.storage.shapefile;
+package org.apache.sis.storage.shapefile.shx;
diff --git 
a/incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/Snippets.java
 
b/incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/Snippets.java
new file mode 100644
index 0000000000..99f9057133
--- /dev/null
+++ 
b/incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/Snippets.java
@@ -0,0 +1,106 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.stream.Stream;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.filter.DefaultFilterFactory;
+import org.apache.sis.geometry.GeneralEnvelope;
+import org.apache.sis.referencing.CommonCRS;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.FeatureQuery;
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.Point;
+import org.opengis.feature.Feature;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.BinarySpatialOperator;
+import org.opengis.filter.FilterFactory;
+
+/**
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+final class Snippets {
+
+    public void read() throws IllegalArgumentException, DataStoreException, 
IOException{
+        // @start region="read"
+        //open datastore
+        try (ShapefileStore store = new 
ShapefileStore(Paths.get("/path/to/file.shp"))) {
+
+            //print feature type
+            System.out.println(store.getType());
+
+            //print all features
+            try (Stream<Feature> features = store.features(false)) {
+                features.forEach(System.out::println);
+            }
+
+            //print only features in envelope and only selected attributes
+            GeneralEnvelope bbox = new 
GeneralEnvelope(CommonCRS.WGS84.normalizedGeographic());
+            bbox.setRange(0, -10, 30);
+            bbox.setRange(1, 45, 55);
+            FilterFactory<Feature, Object, Object> ff = 
DefaultFilterFactory.forFeatures();
+            BinarySpatialOperator<Feature> bboxFilter = 
ff.bbox(ff.property("geometry"), bbox);
+
+            FeatureQuery query = new FeatureQuery();
+            query.setProjection("att1", "att4", "att5");
+            query.setSelection(bboxFilter);
+
+            //print selected features
+            try (Stream<Feature> features = 
store.subset(query).features(false)) {
+                features.forEach(System.out::println);
+            }
+
+        }
+        // @end
+
+    }
+
+    public void write() throws IllegalArgumentException, DataStoreException, 
IOException{
+        // @start region="write"
+        //open a channel
+        try (ShapefileStore store = new 
ShapefileStore(Paths.get("/path/to/file.shp"))) {
+
+            //define the feature type
+            FeatureTypeBuilder ftb = new FeatureTypeBuilder();
+            ftb.setName("test");
+            ftb.addAttribute(Integer.class).setName("id");
+            ftb.addAttribute(String.class).setName("text");
+            
ftb.addAttribute(Point.class).setName("geometry").setCRS(CommonCRS.WGS84.geographic());
+            FeatureType type = ftb.build();
+
+            store.updateType(type);
+            type = store.getType();
+
+            //create features
+            GeometryFactory gf = new GeometryFactory();
+            Feature feature = type.newInstance();
+            feature.setPropertyValue("geometry", gf.createPoint(new 
Coordinate(10,20)));
+            feature.setPropertyValue("id", 1);
+            feature.setPropertyValue("text", "some text 1");
+
+            //add feature in the store
+            store.add(List.of(feature).iterator());
+        }
+        // @end
+    }
+
+}
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 e94274e828..bf86fae187 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
@@ -66,9 +66,9 @@ public class DBFIOTest {
 
         try (DBFReader reader = new DBFReader(cdi, StandardCharsets.UTF_8, 
null)) {
             final DBFHeader header = reader.getHeader();
-            assertEquals(123, header.year);
-            assertEquals(10, header.month);
-            assertEquals(27, header.day);
+            assertEquals(123, header.lastUpdate.getYear()-1900);
+            assertEquals(10, header.lastUpdate.getMonthValue());
+            assertEquals(27, header.lastUpdate.getDayOfMonth());
             assertEquals(2, header.nbRecord);
             assertEquals(193, header.headerSize);
             assertEquals(120, header.recordSize);
@@ -100,18 +100,18 @@ public class DBFIOTest {
             assertEquals(0,    header.fields[4].fieldDecimals);
 
 
-            final DBFRecord record1 = reader.next();
-            assertEquals(1L, record1.fields[0]);
-            assertEquals("text1", record1.fields[1]);
-            assertEquals(10L, record1.fields[2]);
-            assertEquals(20.0, record1.fields[3]);
-            assertEquals(LocalDate.of(2023, 10, 27), record1.fields[4]);
+            final Object[] record1 = reader.next();
+            assertEquals(1L, record1[0]);
+            assertEquals("text1", record1[1]);
+            assertEquals(10L, record1[2]);
+            assertEquals(20.0, record1[3]);
+            assertEquals(LocalDate.of(2023, 10, 27), record1[4]);
 
-            final DBFRecord record2 = reader.next();
-            assertEquals(2L, record2.fields[0]);
-            assertEquals(40L, record2.fields[2]);
-            assertEquals(60.0, record2.fields[3]);
-            assertEquals(LocalDate.of(2023, 10, 28), record2.fields[4]);
+            final Object[] record2 = reader.next();
+            assertEquals(2L, record2[0]);
+            assertEquals(40L, record2[2]);
+            assertEquals(60.0, record2[3]);
+            assertEquals(LocalDate.of(2023, 10, 28), record2[4]);
 
             //no more records
             assertNull(reader.next());
@@ -138,10 +138,10 @@ public class DBFIOTest {
             try (DBFReader reader = new DBFReader(cdi, 
StandardCharsets.US_ASCII, null);
                  DBFWriter writer = new DBFWriter(cdo)) {
 
-                writer.write(reader.getHeader());
+                writer.writeHeader(reader.getHeader());
 
-                for (DBFRecord record = reader.next(); record != null; record 
= reader.next()) {
-                    writer.write(record);
+                for (Object[] record = reader.next(); record != null; record = 
reader.next()) {
+                    writer.writeRecord(record);
                 }
             }
 
@@ -166,13 +166,13 @@ public class DBFIOTest {
         try (DBFReader reader = new DBFReader(cdi, StandardCharsets.UTF_8, new 
int[]{1,3})) {
             final DBFHeader header = reader.getHeader();
 
-            final DBFRecord record1 = reader.next();
-            assertEquals("text1", record1.fields[0]);
-            assertEquals(20.0, record1.fields[1]);
+            final Object[] record1 = reader.next();
+            assertEquals("text1", record1[0]);
+            assertEquals(20.0, record1[1]);
 
-            final DBFRecord record2 = reader.next();
-            assertEquals("text2", record2.fields[0]);
-            assertEquals(60.0, record2.fields[1]);
+            final Object[] record2 = reader.next();
+            assertEquals("text2", record2[0]);
+            assertEquals(60.0, record2[1]);
 
             //no more records
             assertNull(reader.next());
diff --git 
a/incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/dbf/Snippets.java
 
b/incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/dbf/Snippets.java
new file mode 100644
index 0000000000..0813919903
--- /dev/null
+++ 
b/incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/dbf/Snippets.java
@@ -0,0 +1,95 @@
+/*
+ * 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.nio.file.OpenOption;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.time.LocalDate;
+import org.apache.sis.io.stream.ChannelDataInput;
+import org.apache.sis.io.stream.ChannelDataOutput;
+import org.apache.sis.setup.OptionKey;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.StorageConnector;
+
+/**
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+final class Snippets {
+
+    public void read() throws IllegalArgumentException, DataStoreException, 
IOException{
+        // @start region="read"
+        //open a channel
+        StorageConnector cnx = new 
StorageConnector(Paths.get("/path/to/file.dbf"));
+        ChannelDataInput channel = cnx.getStorageAs(ChannelDataInput.class);
+        try (DBFReader reader = new DBFReader(channel, StandardCharsets.UTF_8, 
null)) {
+
+            //print the DBase fields
+            DBFHeader header = reader.getHeader();
+            for (DBFField field : header.fields) {
+                System.out.println(field);
+            }
+
+            //iterate over records
+            for (Object[] record = reader.next(); record != null; record = 
reader.next()){
+
+                if (record == DBFReader.DELETED_RECORD) {
+                    //a deleted record, those should be ignored
+                    continue;
+                }
+
+                //print record values
+                for (int i = 0; i < header.fields.length; i++) {
+                    System.out.println(header.fields[i].fieldName + " : " + 
record[i]);
+                }
+            }
+        }
+        // @end
+    }
+
+    public void write() throws IllegalArgumentException, DataStoreException, 
IOException{
+        // @start region="write"
+        //open a channel
+        StorageConnector cnx = new 
StorageConnector(Paths.get("/path/to/file.dbf"));
+        cnx.setOption(OptionKey.OPEN_OPTIONS, new 
OpenOption[]{StandardOpenOption.WRITE, StandardOpenOption.CREATE, 
StandardOpenOption.TRUNCATE_EXISTING});
+        ChannelDataOutput channel = cnx.getStorageAs(ChannelDataOutput.class);
+
+        //define the header
+        Charset charset = StandardCharsets.UTF_8;
+        DBFHeader header = new DBFHeader();
+        header.lastUpdate = LocalDate.now();
+        header.fields = new DBFField[] {
+          new DBFField("id", DBFField.TYPE_NUMBER, 0, 8, 0, charset),
+          new DBFField("desc", DBFField.TYPE_CHAR, 0, 255, 0, charset),
+          new DBFField("value", DBFField.TYPE_NUMBER, 0, 11, 6, charset)
+        };
+
+        //write records
+        try (DBFWriter writer = new DBFWriter(channel)) {
+            writer.writeHeader(header);
+            writer.writeRecord(1, "A short description", 3.14);
+            writer.writeRecord(2, "Another short description", 123.456);
+            // ... more records
+        }
+        // @end
+    }
+
+}
diff --git 
a/incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/shp/ShapeIOTest.java
 
b/incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/shp/ShapeIOTest.java
index cb36bd7458..0cd236e313 100644
--- 
a/incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/shp/ShapeIOTest.java
+++ 
b/incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/shp/ShapeIOTest.java
@@ -79,10 +79,10 @@ public class ShapeIOTest {
             try (ShapeReader reader = new ShapeReader(cdi, null);
                  ShapeWriter writer = new ShapeWriter(cdo)) {
 
-                writer.write(reader.getHeader());
+                writer.writeHeader(reader.getHeader());
 
                 for (ShapeRecord record = reader.next(); record != null; 
record = reader.next()) {
-                    writer.write(record);
+                    writer.writeRecord(record);
                 }
             }
 
diff --git 
a/incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/shp/Snippets.java
 
b/incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/shp/Snippets.java
new file mode 100644
index 0000000000..9ba5ee2e12
--- /dev/null
+++ 
b/incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/shp/Snippets.java
@@ -0,0 +1,81 @@
+/*
+ * 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.shp;
+
+import java.io.IOException;
+import java.nio.file.OpenOption;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import org.apache.sis.io.stream.ChannelDataInput;
+import org.apache.sis.io.stream.ChannelDataOutput;
+import org.apache.sis.setup.OptionKey;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.StorageConnector;
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.GeometryFactory;
+
+/**
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+final class Snippets {
+
+    public void read() throws IllegalArgumentException, DataStoreException, 
IOException{
+        // @start region="read"
+        //open a channel
+        StorageConnector cnx = new 
StorageConnector(Paths.get("/path/to/file.shp"));
+        ChannelDataInput channel = cnx.getStorageAs(ChannelDataInput.class);
+        try (ShapeReader reader = new ShapeReader(channel, null)) {
+
+            //print the DBase fields
+            ShapeHeader header = reader.getHeader();
+            System.out.println(header.shapeType);
+
+            //iterate over records
+            for (ShapeRecord record = reader.next(); record != null; record = 
reader.next()){
+                System.out.println(record.recordNumber);
+                System.out.println(record.bbox);
+                System.out.println(record.geometry.toText());
+            }
+        }
+        // @end
+    }
+
+    public void write() throws IllegalArgumentException, DataStoreException, 
IOException{
+        // @start region="write"
+        //open a channel
+        StorageConnector cnx = new 
StorageConnector(Paths.get("/path/to/file.shp"));
+        cnx.setOption(OptionKey.OPEN_OPTIONS, new 
OpenOption[]{StandardOpenOption.WRITE, StandardOpenOption.CREATE, 
StandardOpenOption.TRUNCATE_EXISTING});
+        ChannelDataOutput channel = cnx.getStorageAs(ChannelDataOutput.class);
+
+        //define the header
+        ShapeHeader header = new ShapeHeader();
+        header.shapeType = ShapeType.POINT;
+
+        //write records
+        GeometryFactory gf = new GeometryFactory();
+        try (ShapeWriter writer = new ShapeWriter(channel)) {
+            writer.writeHeader(header);
+            int recordNumber = 1;
+            writer.writeRecord(recordNumber++, gf.createPoint(new 
Coordinate(10,20)));
+            writer.writeRecord(recordNumber++, gf.createPoint(new 
Coordinate(-7, 45)));
+            // ... more records
+        }
+        // @end
+    }
+
+}


Reply via email to