sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1770274 [2/2] - in /sis/branches/JDK8: core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/distribution/ core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/ core/sis-referencing/src/test/java/org/apache/sis/test/integratio...
Date Thu, 17 Nov 2016 20:14:36 GMT
Modified: sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java?rev=1770274&r1=1770273&r2=1770274&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java [UTF-8] Thu Nov 17 20:14:36 2016
@@ -16,16 +16,18 @@
  */
 package org.apache.sis.storage.geotiff;
 
-import java.util.Locale;
 import java.io.IOException;
 import java.text.ParseException;
 import java.util.Arrays;
+import java.util.Locale;
 import java.util.logging.Level;
 import java.util.logging.LogRecord;
+import java.nio.charset.Charset;
 import javax.measure.Unit;
 import javax.measure.quantity.Length;
 import org.opengis.metadata.citation.DateType;
 import org.apache.sis.internal.geotiff.Resources;
+import org.apache.sis.internal.storage.ChannelDataInput;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.DataStoreContentException;
 import org.apache.sis.internal.storage.MetadataBuilder;
@@ -54,6 +56,12 @@ final class ImageFileDirectory {
     private static final byte TILE = 1, STRIP = 2;
 
     /**
+     * The GeoTIFF reader which contain this {@code ImageFileDirectory}.
+     * Used for fetching information like the input channel and where to report warnings.
+     */
+    private final Reader reader;
+
+    /**
      * {@code true} if this {@code ImageFileDirectory} has not yet read all deferred entries.
      * When this flag is {@code true}, the {@code ImageFileDirectory} is not yet ready for use.
      */
@@ -250,6 +258,11 @@ final class ImageFileDirectory {
     private short cellWidth = -1, cellHeight = -1;
 
     /**
+     * The minimum or maximum sample value found in the image, or {@code NaN} if unspecified.
+     */
+    private double minValue = Double.NaN, maxValue = Double.NaN;
+
+    /**
      * The number of pixels per {@link #resolutionUnit} in the {@link #imageWidth} and the {@link #imageHeight}
      * directions, or {@link Double#NaN} is unspecified. Since ISO 19115 does not have separated resolution fields
      * for image width and height, Apache SIS stores only the maximal value.
@@ -271,18 +284,36 @@ final class ImageFileDirectory {
 
     /**
      * Creates a new image file directory.
+     *
+     * @param reader  information about the input stream to read, the metadata and the character encoding.
      */
-    ImageFileDirectory() {
+    ImageFileDirectory(final Reader reader) {
+        this.reader = reader;
     }
 
     /**
-     * Adds the value read from the current position in the given stream
-     * for the entry identified by the given GeoTIFF tag.
-     *
-     * @param  reader   the input stream to read, the metadata and the character encoding.
-     * @param  tag      the GeoTIFF tag to decode.
-     * @param  type     the GeoTIFF type of the value to read.
-     * @param  count    the number of values to read.
+     * Shortcut for a frequently requested information.
+     */
+    private ChannelDataInput input() {
+        return reader.input;
+    }
+
+    /**
+     * Shortcut for a frequently requested information.
+     */
+    private Charset encoding() {
+        return reader.owner.encoding;
+    }
+
+    /**
+     * Adds the value read from the current position in the given stream for the entry identified
+     * by the given GeoTIFF tag. This method may store the value either in a field of this class,
+     * or directly in the {@link MetadataBuilder}. However in the later case, this method should
+     * not write anything under the {@code "metadata/contentInfo"} node.
+     *
+     * @param  tag    the GeoTIFF tag to decode.
+     * @param  type   the GeoTIFF type of the value to read.
+     * @param  count  the number of values to read.
      * @return {@code null} on success, or the unrecognized value otherwise.
      * @throws IOException if an error occurred while reading the stream.
      * @throws ParseException if the value need to be parsed as date and the parsing failed.
@@ -292,7 +323,7 @@ final class ImageFileDirectory {
      * @throws UnsupportedOperationException if the given type is {@link Type#UNDEFINED}.
      * @throws DataStoreException if a logical error is found or an unsupported TIFF feature is used.
      */
-    Object addEntry(final Reader reader, final int tag, final Type type, final long count)
+    Object addEntry(final int tag, final Type type, final long count)
             throws IOException, ParseException, DataStoreException
     {
         switch (tag) {
@@ -310,7 +341,7 @@ final class ImageFileDirectory {
              * 2 = Planar format. For example one plane of Red components, one plane of Green and one plane if Blue.
              */
             case Tags.PlanarConfiguration: {
-                final int value = type.readInt(reader.input, count);
+                final int value = type.readInt(input(), count);
                 switch (value) {
                     case 1:  isPlanar = false; break;
                     case 2:  isPlanar = true;  break;
@@ -322,30 +353,30 @@ final class ImageFileDirectory {
              * The number of columns in the image, i.e., the number of pixels per row.
              */
             case Tags.ImageWidth: {
-                imageWidth = type.readUnsignedLong(reader.input, count);
+                imageWidth = type.readUnsignedLong(input(), count);
                 break;
             }
             /*
              * The number of rows of pixels in the image.
              */
             case Tags.ImageLength: {
-                imageHeight = type.readUnsignedLong(reader.input, count);
+                imageHeight = type.readUnsignedLong(input(), count);
                 break;
             }
             /*
              * The tile width in pixels. This is the number of columns in each tile.
              */
             case Tags.TileWidth: {
-                setTileTagFamily(reader, TILE);
-                tileWidth = type.readInt(reader.input, count);
+                setTileTagFamily(TILE);
+                tileWidth = type.readInt(input(), count);
                 break;
             }
             /*
              * The tile length (height) in pixels. This is the number of rows in each tile.
              */
             case Tags.TileLength: {
-                setTileTagFamily(reader, TILE);
-                tileHeight = type.readInt(reader.input, count);
+                setTileTagFamily(TILE);
+                tileHeight = type.readInt(input(), count);
                 break;
             }
             /*
@@ -353,16 +384,16 @@ final class ImageFileDirectory {
              * From this point of view, TileLength = RowPerStrip and TileWidth = ImageWidth.
              */
             case Tags.RowsPerStrip: {
-                setTileTagFamily(reader, STRIP);
-                tileHeight = type.readInt(reader.input, count);
+                setTileTagFamily(STRIP);
+                tileHeight = type.readInt(input(), count);
                 break;
             }
             /*
              * The tile length (height) in pixels. This is the number of rows in each tile.
              */
             case Tags.TileOffsets: {
-                setTileTagFamily(reader, TILE);
-                tileOffsets = type.readVector(reader.input, count);
+                setTileTagFamily(TILE);
+                tileOffsets = type.readVector(input(), count);
                 break;
             }
             /*
@@ -370,16 +401,16 @@ final class ImageFileDirectory {
              * In Apache SIS implementation, strips are considered as a special kind of tiles.
              */
             case Tags.StripOffsets: {
-                setTileTagFamily(reader, STRIP);
-                tileOffsets = type.readVector(reader.input, count);
+                setTileTagFamily(STRIP);
+                tileOffsets = type.readVector(input(), count);
                 break;
             }
             /*
              * The tile width in pixels. This is the number of columns in each tile.
              */
             case Tags.TileByteCounts: {
-                setTileTagFamily(reader, TILE);
-                tileByteCounts = type.readVector(reader.input, count);
+                setTileTagFamily(TILE);
+                tileByteCounts = type.readVector(input(), count);
                 break;
             }
             /*
@@ -387,8 +418,8 @@ final class ImageFileDirectory {
              * In Apache SIS implementation, strips are considered as a special kind of tiles.
              */
             case Tags.StripByteCounts: {
-                setTileTagFamily(reader, STRIP);
-                tileByteCounts = type.readVector(reader.input, count);
+                setTileTagFamily(STRIP);
+                tileByteCounts = type.readVector(input(), count);
                 break;
             }
 
@@ -403,7 +434,7 @@ final class ImageFileDirectory {
              * Compression scheme used on the image data.
              */
             case Tags.Compression: {
-                final long value = type.readLong(reader.input, count);
+                final long value = type.readLong(input(), count);
                 compression = Compression.valueOf(value);
                 if (compression == null) {
                     return value;                           // Cause a warning to be reported by the caller.
@@ -415,7 +446,7 @@ final class ImageFileDirectory {
              * bits order shall be reversed in every bytes before decompression.
              */
             case Tags.FillOrder: {
-                final int value = type.readInt(reader.input, count);
+                final int value = type.readInt(input(), count);
                 switch (value) {
                     case 1: reverseBitsOrder = false; break;
                     case 2: reverseBitsOrder = true;  break;
@@ -429,7 +460,7 @@ final class ImageFileDirectory {
              * But the TIFF specification allows different values.
              */
             case Tags.BitsPerSample: {
-                final Vector values = type.readVector(reader.input, count);
+                final Vector values = type.readVector(input(), count);
                 /*
                  * The current implementation requires that all 'bitsPerSample' elements have the same value.
                  * This restriction may be revisited in future Apache SIS versions.
@@ -440,7 +471,7 @@ final class ImageFileDirectory {
                 for (int i = 1; i < length; i++) {
                     if (values.shortValue(i) != bitsPerSample) {
                         throw new DataStoreContentException(reader.resources().getString(
-                                Resources.Keys.ConstantValueRequired_3, "BitsPerSample", reader.input.filename, values));
+                                Resources.Keys.ConstantValueRequired_3, "BitsPerSample", input().filename, values));
                     }
                 }
                 break;
@@ -450,7 +481,7 @@ final class ImageFileDirectory {
              * and 3 for RGB images. Default value is 1.
              */
             case Tags.SamplesPerPixel: {
-                samplesPerPixel = type.readShort(reader.input, count);
+                samplesPerPixel = type.readShort(input(), count);
                 break;
             }
             /*
@@ -460,7 +491,7 @@ final class ImageFileDirectory {
              * describes the meaning of the extra samples. It may be an alpha channel, but not necessarily.
              */
             case Tags.ExtraSamples: {
-                extraSamples = type.readVector(reader.input, count);
+                extraSamples = type.readVector(input(), count);
                 break;
             }
 
@@ -480,7 +511,7 @@ final class ImageFileDirectory {
              * 4 = Transparency Mask the defines an irregularly shaped region of another image in the same TIFF file.
              */
             case Tags.PhotometricInterpretation: {
-                final short value = type.readShort(reader.input, count);
+                final short value = type.readShort(input(), count);
                 if (value < 0 || value > Byte.MAX_VALUE) return value;
                 photometricInterpretation = (byte) value;
                 break;
@@ -493,14 +524,15 @@ final class ImageFileDirectory {
              * (black is 0,0,0) and 65535 represents the maximum intensity.
              */
             case Tags.ColorMap: {
-                colorMap = type.readVector(reader.input, count);
+                colorMap = type.readVector(input(), count);
                 break;
             }
             /*
              * The minimum component value used. Default is 0.
              */
             case Tags.MinSampleValue: {
-                reader.metadata.addMinimumSampleValue(type.readDouble(reader.input, count));
+                final double v = type.readDouble(input(), count);
+                if (Double.isNaN(minValue) || v < minValue) minValue = v;
                 break;
             }
             /*
@@ -509,7 +541,8 @@ final class ImageFileDirectory {
              * visual appearance of an image, unless a map styling is applied.
              */
             case Tags.MaxSampleValue: {
-                reader.metadata.addMaximumSampleValue(type.readDouble(reader.input, count));
+                final double v = type.readDouble(input(), count);
+                if (Double.isNaN(maxValue) || v > maxValue) maxValue = v;
                 break;
             }
 
@@ -555,21 +588,21 @@ final class ImageFileDirectory {
              * The first 4 values are special, and contain GeoKey directory header information.
              */
             case Tags.GeoKeyDirectoryTag : {
-                reader.crsBuilder.setGeoKeyDirectoryTag(type.readVector(reader.input, count));
+                reader.crsBuilder.setGeoKeyDirectoryTag(type.readVector(input(), count));
                 break;
             }
             /*
              * This tag is used to store all of the DOUBLE valued GeoKeys, referenced by the GeoKeyDirectoryTag.
              */
             case Tags.GeoDoubleParamsTag : {
-                reader.crsBuilder.setGeoDoubleParamsTag(type.readVector(reader.input, count));
+                reader.crsBuilder.setGeoDoubleParamsTag(type.readVector(input(), count));
                 break;
             }
             /*
              * This tag is used to store all of the ASCII valued GeoKeys, referenced by the GeoKeyDirectoryTag.
              */
             case Tags.GeoAsciiParamsTag : {
-                final String[] values = type.readString(reader.input, count, reader.owner.encoding);
+                final String[] values = type.readString(input(), count, encoding());
                 reader.crsBuilder.setGeoAsciiParamsTag(values[0]);      // TODO: should pass the full array.
                 break;
             }
@@ -586,6 +619,7 @@ final class ImageFileDirectory {
             ////                                                                                        ////
             ////    Metadata for discovery purposes, conditions of use, etc.                            ////
             ////    Those metadata are not "critical" information for reading the image.                ////
+            ////    Should not write anything under 'metadata/contentInfo' node.                        ////
             ////                                                                                        ////
             ////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -594,7 +628,7 @@ final class ImageFileDirectory {
              * For example, a user may wish to attach a comment such as "1988 company picnic" to an image.
              */
             case Tags.ImageDescription: {
-                for (final String value : type.readString(reader.input, count, reader.owner.encoding)) {
+                for (final String value : type.readString(input(), count, encoding())) {
                     reader.metadata.addTitle(value);
                 }
                 break;
@@ -604,7 +638,7 @@ final class ImageFileDirectory {
              * Copyright information, but Apache SIS does not support this legacy practice.
              */
             case Tags.Artist: {
-                for (final String value : type.readString(reader.input, count, reader.owner.encoding)) {
+                for (final String value : type.readString(input(), count, encoding())) {
                     reader.metadata.addAuthor(value);
                 }
                 break;
@@ -614,7 +648,7 @@ final class ImageFileDirectory {
              * Example: “Copyright, John Smith, 1992. All rights reserved.”
              */
             case Tags.Copyright: {
-                for (final String value : type.readString(reader.input, count, reader.owner.encoding)) {
+                for (final String value : type.readString(input(), count, encoding())) {
                     reader.metadata.parseLegalNotice(value);
                 }
                 break;
@@ -623,7 +657,7 @@ final class ImageFileDirectory {
              * Date and time of image creation. The format is: "YYYY:MM:DD HH:MM:SS" with 24-hour clock.
              */
             case Tags.DateTime: {
-                for (final String value : type.readString(reader.input, count, reader.owner.encoding)) {
+                for (final String value : type.readString(input(), count, encoding())) {
                     reader.metadata.add(reader.getDateFormat().parse(value), DateType.CREATION);
                 }
                 break;
@@ -632,8 +666,8 @@ final class ImageFileDirectory {
              * The computer and/or operating system in use at the time of image creation.
              */
             case Tags.HostComputer: {
-                for (final String value : type.readString(reader.input, count, reader.owner.encoding)) {
-                    reader.metadata.addProcessingPlatform(value);
+                for (final String value : type.readString(input(), count, encoding())) {
+                    reader.metadata.addHostComputer(value);
                 }
                 break;
             }
@@ -641,8 +675,8 @@ final class ImageFileDirectory {
              * Name and version number of the software package(s) used to create the image.
              */
             case Tags.Software: {
-                for (final String value : type.readString(reader.input, count, reader.owner.encoding)) {
-                    reader.metadata.addSoftwareReferences(value);
+                for (final String value : type.readString(input(), count, encoding())) {
+                    reader.metadata.addSoftwareReference(value);
                 }
                 break;
             }
@@ -660,7 +694,7 @@ final class ImageFileDirectory {
              * generate the image.
              */
             case Tags.Model: {
-                for (final String value : type.readString(reader.input, count, reader.owner.encoding)) {
+                for (final String value : type.readString(input(), count, encoding())) {
                     reader.metadata.addInstrument(value);
                 }
                 break;
@@ -670,7 +704,7 @@ final class ImageFileDirectory {
              */
             case Tags.XResolution:
             case Tags.YResolution: {
-                final double r = type.readDouble(reader.input, count);
+                final double r = type.readDouble(input(), count);
                 if (Double.isNaN(resolution) || r > resolution) {
                     resolution = r;
                 }
@@ -684,7 +718,7 @@ final class ImageFileDirectory {
              *   3 = Centimeter.
              */
             case Tags.ResolutionUnit: {
-                final short unit = type.readShort(reader.input, count);
+                final short unit = type.readShort(input(), count);
                 switch (unit) {
                     case 1:  resolutionUnit = null;             break;
                     case 2:  resolutionUnit = Units.INCH;       break;
@@ -702,7 +736,7 @@ final class ImageFileDirectory {
              *   3 = A randomized process such as error diffusion has been applied to the image data.
              */
             case Tags.Threshholding: {
-                final short value = type.readShort(reader.input, count);
+                final short value = type.readShort(input(), count);
                 switch (value) {
                     case 1:  break;
                     case 2:  if (cellWidth >= 0 || cellHeight >= 0) return null; else break;
@@ -717,11 +751,11 @@ final class ImageFileDirectory {
              * a dithered or halftoned bilevel file. Meaningful only if Threshholding = 2.
              */
             case Tags.CellWidth: {
-                cellWidth = type.readShort(reader.input, count);
+                cellWidth = type.readShort(input(), count);
                 break;
             }
             case Tags.CellLength: {
-                cellHeight = type.readShort(reader.input, count);
+                cellHeight = type.readShort(input(), count);
                 break;
             }
 
@@ -743,7 +777,7 @@ final class ImageFileDirectory {
              */
             case Tags.GrayResponseCurve:
             case Tags.GrayResponseUnit: {
-                warning(reader, Level.FINE, Resources.Keys.IgnoredTag_1, Tags.name(tag));
+                warning(Level.FINE, Resources.Keys.IgnoredTag_1, Tags.name(tag));
                 break;
             }
         }
@@ -753,14 +787,13 @@ final class ImageFileDirectory {
     /**
      * Sets the {@link #tileTagFamily} field to the given value if it does not conflict with previous value.
      *
-     * @param  reader   the input stream to read.
-     * @param  family   either {@link #TILE} or {@link #STRIP}.
+     * @param  family  either {@link #TILE} or {@link #STRIP}.
      * @throws DataStoreContentException if {@link #tileTagFamily} is already set to another value.
      */
-    private void setTileTagFamily(final Reader reader, final byte family) throws DataStoreContentException {
+    private void setTileTagFamily(final byte family) throws DataStoreContentException {
         if (tileTagFamily != family && tileTagFamily != 0) {
             throw new DataStoreContentException(reader.resources().getString(
-                    Resources.Keys.InconsistentTileStrip_1, reader.input.filename));
+                    Resources.Keys.InconsistentTileStrip_1, input().filename));
         }
         tileTagFamily = family;
     }
@@ -811,9 +844,9 @@ final class ImageFileDirectory {
      *
      * @throws DataStoreContentException if a mandatory tag is missing and can not be inferred.
      */
-    final void validateMandatoryTags(final Reader reader) throws DataStoreContentException {
-        if (imageWidth  < 0) throw missingTag(reader, Tags.ImageWidth);
-        if (imageHeight < 0) throw missingTag(reader, Tags.ImageLength);
+    final void validateMandatoryTags() throws DataStoreContentException {
+        if (imageWidth  < 0) throw missingTag(Tags.ImageWidth);
+        if (imageHeight < 0) throw missingTag(Tags.ImageLength);
         final int offsetsTag, byteCountsTag;
         switch (tileTagFamily) {
             case STRIP: {
@@ -830,22 +863,22 @@ final class ImageFileDirectory {
             }
             default: {
                 throw new DataStoreContentException(reader.resources().getString(
-                                Resources.Keys.InconsistentTileStrip_1, reader.input.filename));
+                        Resources.Keys.InconsistentTileStrip_1, input().filename));
             }
         }
         if (tileOffsets == null) {
-            throw missingTag(reader, offsetsTag);
+            throw missingTag(offsetsTag);
         }
         if (samplesPerPixel == 0) {
             samplesPerPixel = 1;
-            missingTag(reader, Tags.SamplesPerPixel, 1, false);
+            missingTag(Tags.SamplesPerPixel, 1, false);
         }
         if (bitsPerSample == 0) {
             bitsPerSample = 1;
-            missingTag(reader, Tags.BitsPerSample, 1, false);
+            missingTag(Tags.BitsPerSample, 1, false);
         }
         if (colorMap != null) {
-            ensureSameLength(reader, Tags.ColorMap, Tags.BitsPerSample, colorMap.size(),  3 * (1 << bitsPerSample));
+            ensureSameLength(Tags.ColorMap, Tags.BitsPerSample, colorMap.size(),  3 * (1 << bitsPerSample));
         }
         /*
          * All of tile width, height and length information should be provided. But if only one of them is missing,
@@ -864,12 +897,12 @@ final class ImageFileDirectory {
             }
             case 0b0001: {          // Compute missing tile width.
                 tileWidth = computeTileSize(tileHeight);
-                missingTag(reader, Tags.TileWidth, tileWidth, true);
+                missingTag(Tags.TileWidth, tileWidth, true);
                 break;
             }
             case 0b0010: {          // Compute missing tile height.
                 tileHeight = computeTileSize(tileWidth);
-                missingTag(reader, Tags.TileLength, tileHeight, true);
+                missingTag(Tags.TileLength, tileHeight, true);
                 break;
             }
             case 0b0100: {          // Compute missing tile byte count.
@@ -877,7 +910,7 @@ final class ImageFileDirectory {
                 final long[] tileByteCountArray = new long[tileOffsets.size()];
                 Arrays.fill(tileByteCountArray, tileByteCount);
                 tileByteCounts = Vector.create(tileByteCountArray, true);
-                missingTag(reader, byteCountsTag, tileByteCount, true);
+                missingTag(byteCountsTag, tileByteCount, true);
                 break;
             }
             default: {
@@ -887,7 +920,7 @@ final class ImageFileDirectory {
                     case 0b0010: tag = Tags.TileLength; break;
                     default:     tag = byteCountsTag;   break;
                 }
-                throw missingTag(reader, tag);
+                throw missingTag(tag);
             }
         }
         /*
@@ -895,7 +928,7 @@ final class ImageFileDirectory {
          * ensure that the number of tiles is equal to the expected number.  The formula below is the one
          * documented in the TIFF specification and reproduced in tileWidth & tileHeight fields javadoc.
          */
-        ensureSameLength(reader, offsetsTag, byteCountsTag, tileOffsets.size(), tileByteCounts.size());
+        ensureSameLength(offsetsTag, byteCountsTag, tileOffsets.size(), tileByteCounts.size());
         long expectedCount = Math.multiplyExact(
                 Math.addExact(imageWidth,  tileWidth  - 1) / tileWidth,
                 Math.addExact(imageHeight, tileHeight - 1) / tileHeight);
@@ -905,18 +938,27 @@ final class ImageFileDirectory {
         final int actualCount = Math.min(tileOffsets.size(), tileByteCounts.size());
         if (actualCount != expectedCount) {
             throw new DataStoreContentException(reader.resources().getString(Resources.Keys.UnexpectedTileCount_3,
-                    reader.input.filename, expectedCount, actualCount));
+                    input().filename, expectedCount, actualCount));
         }
     }
 
     /**
      * Completes the metadata with the information stored in the field of this IFD.
      * This method is invoked only if the user requested the ISO 19115 metadata.
+     * This method creates a new {@code "metadata/contentInfo"} node for this image.
+     * Information not under the {@code "metadata/contentInfo"} node will be merged
+     * with the current content of the given {@code MetadataBuilder}.
+     *
+     * @param metadata  where to write metadata information. Caller should have already invoked
+     *        {@link MetadataBuilder#setFormat(String)} before {@code completeMetadata(…)} calls.
      */
     final void completeMetadata(final MetadataBuilder metadata, final Locale locale) {
+        metadata.newCoverage(false);
         if (compression != null) {
             metadata.addCompression(compression.name().toLowerCase(locale));
         }
+        metadata.addMinimumSampleValue(minValue);
+        metadata.addMaximumSampleValue(maxValue);
         /*
          * Add the resolution into the metadata. Our current ISO 19115 implementation restricts
          * the resolution unit to metres, but it may be relaxed in a future SIS version.
@@ -938,11 +980,11 @@ final class ImageFileDirectory {
                 break;
             }
             case -3: {
-                metadata.setProcessingDocumentation(Resources.formatInternational(Resources.Keys.RandomizedProcessApplied));
+                metadata.addProcessDescription(Resources.formatInternational(Resources.Keys.RandomizedProcessApplied));
                 break;
             }
             default: {
-                metadata.setProcessingDocumentation(Resources.formatInternational(
+                metadata.addProcessDescription(Resources.formatInternational(
                             Resources.Keys.DitheringOrHalftoningApplied_2,
                             (cellWidth  >= 0) ? cellWidth  : '?',
                             (cellHeight >= 0) ? cellHeight : '?'));
@@ -954,12 +996,11 @@ final class ImageFileDirectory {
     /**
      * Reports a warning with a message created from the given resource keys and parameters.
      *
-     * @param reader      the reader where to send the warning message.
      * @param level       the logging level for the message to log.
      * @param key         the {@code Resources} key of the message to format.
      * @param parameters  the parameters to put in the message.
      */
-    private void warning(final Reader reader, final Level level, final short key, final Object... parameters) {
+    private void warning(final Level level, final short key, final Object... parameters) {
         final LogRecord r = reader.resources().getLogRecord(level, key, parameters);
         reader.owner.warning(r);
     }
@@ -967,22 +1008,21 @@ final class ImageFileDirectory {
     /**
      * Verifies that the given tags have the same length and reports a warning if they do not.
      */
-    private void ensureSameLength(final Reader reader, final int tag1, final int tag2, final int length1, final int length2) {
+    private void ensureSameLength(final int tag1, final int tag2, final int length1, final int length2) {
         if (length1 != length2) {
-            warning(reader, Level.WARNING, Resources.Keys.MismatchedLength_4, Tags.name(tag1), Tags.name(tag2), length1, length2);
+            warning(Level.WARNING, Resources.Keys.MismatchedLength_4, Tags.name(tag1), Tags.name(tag2), length1, length2);
         }
     }
 
     /**
      * Reports a warning for a missing TIFF tag for which a default value can be computed.
      *
-     * @param  reader    the TIFF reader, used for fetching resources and file name.
      * @param  missing   the numerical value of the missing tag.
      * @param  value     the default value or the computed value.
      * @param  computed  whether the default value has been computed.
      */
-    private void missingTag(final Reader reader, final int missing, final long value, final boolean computed) {
-        warning(reader, computed ? Level.WARNING : Level.FINE,
+    private void missingTag(final int missing, final long value, final boolean computed) {
+        warning(computed ? Level.WARNING : Level.FINE,
                 computed ? Resources.Keys.ComputedValueForAttribute_2 : Resources.Keys.DefaultValueForAttribute_2,
                 Tags.name(missing), value);
     }
@@ -990,11 +1030,10 @@ final class ImageFileDirectory {
     /**
      * Builds an exception for a missing TIFF tag for which no default value can be computed.
      *
-     * @param  reader   the TIFF reader, used for fetching resources and file name.
      * @param  missing  the numerical value of the missing tag.
      */
-    private DataStoreContentException missingTag(final Reader reader, final int missing) {
+    private DataStoreContentException missingTag(final int missing) {
         return new DataStoreContentException(reader.resources().getString(
-                Resources.Keys.MissingValue_2, reader.input.filename, Tags.name(missing)));
+                Resources.Keys.MissingValue_2, input().filename, Tags.name(missing)));
     }
 }

Modified: sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/Reader.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/Reader.java?rev=1770274&r1=1770273&r2=1770274&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/Reader.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/Reader.java [UTF-8] Thu Nov 17 20:14:36 2016
@@ -111,6 +111,9 @@ final class Reader extends GeoTIFF {
 
     /**
      * Builder for the Coordinate Reference System.
+     *
+     * @todo should not be here since we can have a CRS for each image and we don't want to merge them
+     *       like we do for metadata.
      */
     final TiffCRSBuilder crsBuilder;
 
@@ -129,7 +132,7 @@ final class Reader extends GeoTIFF {
         this.crsBuilder = new TiffCRSBuilder(this);
         /*
          * A TIFF file begins with either "II" (0x4949) or "MM" (0x4D4D) characters.
-         * Those characters identify the byte order. Note we we do not need to care
+         * Those characters identify the byte order. Note that we do not need to care
          * about the byte order for this flag since the two bytes shall have the same value.
          */
         final short order = input.readShort();
@@ -141,7 +144,7 @@ final class Reader extends GeoTIFF {
              * The pointer type is implicitely the Java Integer type (4 bytes).
              *
              * Magic number of BigTIFF file is 43, followed by the pointer size.
-             * Currently, that pointer type canonly be the Java Long type (8 bytes),
+             * Currently, that pointer type can only be the Java Long type (8 bytes),
              * but a future BigTIFF version may allow 16 bytes wide pointers.
              */
             switch (input.readShort()) {
@@ -238,7 +241,7 @@ final class Reader extends GeoTIFF {
              * to get the pointer to the next IFD.
              */
             final int offsetSize = Integer.BYTES << intSizeExpansion;
-            final ImageFileDirectory dir = new ImageFileDirectory();
+            final ImageFileDirectory dir = new ImageFileDirectory(this);
             for (long remaining = readUnsignedShort(); --remaining >= 0;) {
                 /*
                  * Each entry in the Image File Directory has the following format:
@@ -265,7 +268,7 @@ final class Reader extends GeoTIFF {
                              * recommends to ignore it (for allowing them to add new types in the future), or an entry
                              * without value (count = 0) - in principle illegal but we make this reader tolerant.
                              */
-                            error = dir.addEntry(this, tag, type, count);
+                            error = dir.addEntry(tag, type, count);
                         } catch (ParseException | RuntimeException e) {
                             error = e;
                         }
@@ -296,7 +299,7 @@ final class Reader extends GeoTIFF {
             resolveDeferredEntries(dir, Long.MAX_VALUE);
             dir.hasDeferredEntries = false;
         }
-        dir.validateMandatoryTags(this);
+        dir.validateMandatoryTags();
         return dir;
     }
 
@@ -332,7 +335,7 @@ final class Reader extends GeoTIFF {
                 input.seek(Math.addExact(origin, entry.offset));
                 Object error;
                 try {
-                    error = entry.owner.addEntry(this, entry.tag, entry.type, entry.count);
+                    error = entry.owner.addEntry(entry.tag, entry.type, entry.count);
                 } catch (ParseException | RuntimeException e) {
                     error = e;
                 }

Modified: sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/Tags.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/Tags.java?rev=1770274&r1=1770273&r2=1770274&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/Tags.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/Tags.java [UTF-8] Thu Nov 17 20:14:36 2016
@@ -128,19 +128,25 @@ final class Tags {
     /////////////////////////////////////////////////////////
 
     /**
-     * References the needed "GeoKeys" to build CRS.
+     * References all "GeoKeys" needed for building the Coordinate Reference System.
+     * GeoTIFF keys are stored in a kind of directory inside the TIFF directory, with
+     * the keys enumerated in the {@link GeoTIFF} class.
+     *
+     * @todo omit the "Tag" suffix since this class is all about tags.
      */
-    public static final int GeoKeyDirectoryTag = 0x87AF; //-- 34735
+    public static final int GeoKeyDirectoryTag = 0x87AF;        // 34735
 
     /**
-     * This tag is used to store all of the DOUBLE valued GeoKeys, referenced by the GeoKeyDirectoryTag.
+     * References all {@code double} values referenced by the {@link GeoKeys}.
+     * The keys are stored in the entry referenced by {@link #GeoKeyDirectoryTag}.
      */
-    public static final int GeoDoubleParamsTag = 0x87B0; //-- 34736
+    public static final int GeoDoubleParamsTag = 0x87B0;        // 34736
 
     /**
-     * This tag is used to store all of the ASCII valued GeoKeys, referenced by the GeoKeyDirectoryTag.
+     * References all {@link String} values referenced by the {@link GeoKeys}.
+     * The keys are stored in the entry referenced by {@link #GeoKeyDirectoryTag}.
      */
-    public static final int GeoAsciiParamsTag = 0x87B1; //-- 34737
+    public static final int GeoAsciiParamsTag = 0x87B1;         // 34737
 
     /**
      * Do not allow instantiation of this class.

Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java?rev=1770274&r1=1770273&r2=1770274&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java [UTF-8] Thu Nov 17 20:14:36 2016
@@ -35,7 +35,7 @@ import org.opengis.metadata.maintenance.
 import org.opengis.metadata.acquisition.Context;
 import org.opengis.metadata.acquisition.OperationType;
 import org.opengis.metadata.identification.Progress;
-import org.opengis.referencing.ReferenceSystem;
+import org.opengis.metadata.distribution.Format;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.geometry.AbstractEnvelope;
@@ -62,7 +62,6 @@ import org.apache.sis.metadata.iso.citat
 import org.apache.sis.metadata.iso.constraint.DefaultLegalConstraints;
 import org.apache.sis.metadata.iso.identification.DefaultResolution;
 import org.apache.sis.metadata.iso.identification.DefaultDataIdentification;
-import org.apache.sis.metadata.iso.distribution.DefaultDistribution;
 import org.apache.sis.metadata.iso.distribution.DefaultFormat;
 import org.apache.sis.metadata.iso.acquisition.DefaultAcquisitionInformation;
 import org.apache.sis.metadata.iso.acquisition.DefaultEvent;
@@ -70,8 +69,9 @@ import org.apache.sis.metadata.iso.acqui
 import org.apache.sis.metadata.iso.acquisition.DefaultOperation;
 import org.apache.sis.metadata.iso.acquisition.DefaultPlatform;
 import org.apache.sis.metadata.iso.acquisition.DefaultRequirement;
+import org.apache.sis.metadata.sql.MetadataStoreException;
+import org.apache.sis.metadata.sql.MetadataSource;
 import org.apache.sis.util.CharSequences;
-import org.apache.sis.util.Utilities;
 import org.apache.sis.util.iso.Types;
 
 import static java.util.Collections.singleton;
@@ -82,7 +82,6 @@ import java.time.LocalDate;
 import org.apache.sis.metadata.iso.lineage.DefaultLineage;
 import org.apache.sis.metadata.iso.lineage.DefaultProcessStep;
 import org.apache.sis.metadata.iso.lineage.DefaultProcessing;
-import org.apache.sis.util.iso.DefaultInternationalString;
 import org.opengis.feature.FeatureType;
 
 
@@ -190,18 +189,26 @@ public class MetadataBuilder {
 
     /**
      * The distribution format, or {@code null} if none.
+     * This is part of the resource {@linkplain #identification}.
      */
-    private DefaultFormat format;
+    private Format format;
 
     /**
-     * Information about distribution (including the {@linkplain #format}), or {@code null} if none.
+     * Information about the events or source data used in constructing the data specified by the
+     * {@linkplain DefaultLineage#getScope() scope}.
      */
-    private DefaultDistribution distribution;
-
     private DefaultLineage lineage;
 
+    /**
+     * Information about an event or transformation in the life of a resource.
+     * This is part of {@link #lineage}.
+     */
     private DefaultProcessStep processStep;
 
+    /**
+     * Information about the procedures, processes and algorithms applied in the process step.
+     * This is part of {@link #processStep}.
+     */
     private DefaultProcessing processing;
 
     /**
@@ -237,6 +244,24 @@ public class MetadataBuilder {
     }
 
     /**
+     * Adds the given element in the given collection if not already present. This method is used only for
+     * properties that are usually stored in {@code List} rather than {@code Set} and for which we do not
+     * keep a reference in this {@code MetadataBuilder} after the element has been added. This method is
+     * intended for adding elements that despite being modifiable, are not going to be modified by this
+     * {@code MetadataBuilder} class. Performance should not be a concern since the given list is usually
+     * very short (0 or 1 element).
+     *
+     * <p>The given element should be non-null. The check for null value should be done by the caller instead
+     * than by this method in order to avoid unneeded creation of collections. Such creation are implicitly
+     * done by calls to {@code metadata.getFoos()} methods.</p>
+     */
+    private static <E> void addIfNotPresent(final Collection<E> collection, final E element) {
+        if (!collection.contains(element)) {
+            collection.add(element);
+        }
+    }
+
+    /**
      * Commits all pending information under the "responsible party" node (author, address, <i>etc</i>).
      * If there is no pending party information, then invoking this method has no effect
      * except setting the {@code type} flag.
@@ -249,7 +274,7 @@ public class MetadataBuilder {
      */
     public final void newParty(final byte type) {
         if (party != null) {
-            responsibility().getParties().add(party);
+            addIfNotPresent(responsibility().getParties(), party);
             party = null;
         }
         partyType = type;
@@ -269,7 +294,7 @@ public class MetadataBuilder {
          */
         newParty((byte) 0);
         if (responsibility != null) {
-            citation().getCitedResponsibleParties().add(responsibility);
+            addIfNotPresent(citation().getCitedResponsibleParties(), responsibility);
             responsibility = null;
         }
         if (citation != null) {
@@ -277,15 +302,19 @@ public class MetadataBuilder {
             citation = null;
         }
         if (extent != null) {
-            identification().getExtents().add(extent);
+            addIfNotPresent(identification().getExtents(), extent);
             extent = null;
         }
+        if (format != null) {
+            addIfNotPresent(identification().getResourceFormats(), format);
+            format = null;
+        }
         if (constraints != null) {
-            identification().getResourceConstraints().add(constraints);
+            addIfNotPresent(identification().getResourceConstraints(), constraints);
             constraints = null;
         }
         if (identification != null) {
-            metadata().getIdentificationInfo().add(identification);
+            addIfNotPresent(metadata().getIdentificationInfo(), identification);
             identification = null;
         }
     }
@@ -300,10 +329,10 @@ public class MetadataBuilder {
      */
     public final void newAcquisition() {
         if (platform != null) {
-            acquisition().getPlatforms().add(platform);
+            addIfNotPresent(acquisition().getPlatforms(), platform);
         }
         if (acquisition != null) {
-            metadata().getAcquisitionInformation().add(acquisition);
+            addIfNotPresent(metadata().getAcquisitionInformation(), acquisition);
             acquisition = null;
         }
     }
@@ -318,10 +347,11 @@ public class MetadataBuilder {
      */
     public final void newGridRepresentation() {
         if (gridRepresentation != null) {
-            if (!gridRepresentation.isEmpty()) {
-                gridRepresentation.setNumberOfDimensions(shared(gridRepresentation.getAxisDimensionProperties().size()));
+            final int n = gridRepresentation.getAxisDimensionProperties().size();
+            if (n != 0) {
+                gridRepresentation.setNumberOfDimensions(shared(n));
             }
-            metadata.getSpatialRepresentationInfo().add(gridRepresentation);
+            addIfNotPresent(metadata.getSpatialRepresentationInfo(), gridRepresentation);
             gridRepresentation = null;
         }
     }
@@ -340,15 +370,15 @@ public class MetadataBuilder {
      */
     public final void newCoverage(final boolean electromagnetic) {
         if (sampleDimension != null) {
-            attributGroup().getAttributes().add(sampleDimension);
+            addIfNotPresent(attributGroup().getAttributes(), sampleDimension);
             sampleDimension = null;
         }
         if (attributeGroup != null) {
-            coverageDescription().getAttributeGroups().add(attributeGroup);
+            addIfNotPresent(coverageDescription().getAttributeGroups(), attributeGroup);
             attributeGroup = null;
         }
         if (coverageDescription != null) {
-            metadata().getContentInfo().add(coverageDescription);
+            addIfNotPresent(metadata().getContentInfo(), coverageDescription);
             coverageDescription = null;
         }
         this.electromagnetic = electromagnetic;
@@ -363,92 +393,35 @@ public class MetadataBuilder {
      */
     public final void newFeatureTypes() {
         if (featureDescription != null) {
-            metadata().getContentInfo().add(featureDescription);
+            addIfNotPresent(metadata().getContentInfo(), featureDescription);
             featureDescription = null;
         }
     }
 
     /**
-     * Commits all pending information under the metadata "distribution info" node (format, <i>etc</i>).
-     * If there is no pending distribution information, then invoking this method has no effect.
-     * If new distribution information are added after this method call, they will be stored in a new element.
-     *
-     * <p>This method does not need to be invoked unless a new "distribution info" node,
-     * separated from the previous one, is desired.</p>
-     */
-    public final void newDistribution() {
-        if (format != null) {
-            distribution().getDistributionFormats().add(format);
-            format = null;
-        }
-        if (distribution != null) {
-            metadata().getDistributionInfo().add(distribution);
-            distribution = null;
-        }
-    }
-
-    /**
-     * Commits all pending information under the metadata "lineage" node (format, <i>etc</i>).
+     * Commits all pending information under the metadata "lineage" node (process steps, <i>etc</i>).
      * If there is no pending lineage information, then invoking this method has no effect.
      * If new lineage information are added after this method call, they will be stored in a new element.
      *
-     * <p>This method does not need to be invoked unless a new "distribution info" node,
+     * <p>This method does not need to be invoked unless a new "lineage" node,
      * separated from the previous one, is desired.</p>
      */
     public final void newLineage() {
         if (processing != null) {
-            processStep().setProcessingInformation(processing());
+            processStep().setProcessingInformation(processing);
             processing = null;
         }
-
         if (processStep != null) {
-            lineage().getProcessSteps().add(processStep());
+            addIfNotPresent(lineage().getProcessSteps(), processStep);
             processStep = null;
         }
-
         if (lineage != null) {
-            metadata().getResourceLineages().add(lineage());
+            addIfNotPresent(metadata().getResourceLineages(), lineage);
             lineage = null;
         }
     }
 
     /**
-     * Creates the lineage object if it does not already exists, then returns it.
-     *
-     * @return the lineage (never {@code null}).
-     */
-    private DefaultLineage lineage() {
-        if (lineage == null) {
-            lineage = new DefaultLineage();
-        }
-        return lineage;
-    }
-
-    /**
-     * Creates the processStep object if it does not already exists, then returns it.
-     *
-     * @return the processStep (never {@code null}).
-     */
-    private DefaultProcessStep processStep() {
-        if (processStep == null) {
-            processStep = new DefaultProcessStep();
-        }
-        return processStep;
-    }
-
-    /**
-     * Creates the processing object if it does not already exists, then returns it.
-     *
-     * @return the processing (never {@code null}).
-     */
-    private DefaultProcessing processing() {
-        if (processing == null) {
-            processing = new DefaultProcessing();
-        }
-        return processing;
-    }
-
-    /**
      * Creates the metadata object if it does not already exists, then returns it.
      *
      * @return the metadata (never {@code null}).
@@ -634,55 +607,75 @@ public class MetadataBuilder {
      * @return the distribution format (never {@code null}).
      */
     private DefaultFormat format() {
-        if (format == null) {
-            format = new DefaultFormat();
+        DefaultFormat df = DefaultFormat.castOrCopy(format);
+        if (df == null) {
+            format = df = new DefaultFormat();
+        }
+        return df;
+    }
+
+    /**
+     * Creates the lineage object if it does not already exists, then returns it.
+     *
+     * @return the lineage (never {@code null}).
+     */
+    private DefaultLineage lineage() {
+        if (lineage == null) {
+            lineage = new DefaultLineage();
         }
-        return format;
+        return lineage;
     }
 
     /**
-     * Creates the distribution information object if it does not already exists, then returns it.
+     * Creates the process step object if it does not already exists, then returns it.
      *
-     * @return the distribution information (never {@code null}).
+     * @return the process step (never {@code null}).
      */
-    private DefaultDistribution distribution() {
-        if (distribution == null) {
-            distribution = new DefaultDistribution();
+    private DefaultProcessStep processStep() {
+        if (processStep == null) {
+            processStep = new DefaultProcessStep();
         }
-        return distribution;
+        return processStep;
     }
 
     /**
-     * Adds the given element in the collection if not already present.
-     * This method is used only for properties that are usually stored in {@code List} rather than {@code Set}
-     * and for which we do not keep a reference in this {@code MetadataBuilder} after the element has been added.
-     * This method is intended for adding elements that despite being modifiable, are not going to be modified by
-     * this {@code MetadataBuilder} class.
+     * Creates the processing object if it does not already exists, then returns it.
+     *
+     * @return the processing (never {@code null}).
      */
-    private static <E> void addIfNotPresent(final Collection<E> collection, final E element) {
-        if (!collection.contains(element)) {
-            collection.add(element);
+    private DefaultProcessing processing() {
+        if (processing == null) {
+            processing = new DefaultProcessing();
         }
+        return processing;
     }
 
     /**
      * Adds a language used for documenting metadata.
+     * Storage location is:
+     *
+     * <pre>metadata/language</pre>
      *
      * @param  language  a language used for documenting metadata.
      */
     public final void add(final Locale language) {
         if (language != null) {
+            // No need to use 'addIfNotPresent(…)' because Locale collection is a Set by default.
             metadata().getLanguages().add(language);
         }
     }
 
     /**
      * Adds the given character encoding to the metadata.
+     * Storage location is:
+     *
+     * <pre>metadata/characterSet</pre>
      *
      * @param  encoding  the character encoding to add.
      */
     public final void add(final Charset encoding) {
         if (encoding != null) {
+            // No need to use 'addIfNotPresent(…)' because Charset collection is a Set by default.
             metadata().getCharacterSets().add(encoding);
         }
     }
@@ -690,17 +683,23 @@ public class MetadataBuilder {
     /**
      * Adds information about the scope of the resource.
      * The scope is typically {@link ScopeCode#DATASET}.
+     * Storage location is:
+     *
+     * <pre>metadata/metadataScope/resourceScope</pre>
      *
      * @param  scope  the scope of the resource, or {@code null} if none.
      */
     public final void add(final ScopeCode scope) {
         if (scope != null) {
-            metadata().getMetadataScopes().add(new DefaultMetadataScope(scope, null));
+            addIfNotPresent(metadata().getMetadataScopes(), new DefaultMetadataScope(scope, null));
         }
     }
 
     /**
      * Adds descriptions for the given feature.
+     * Storage location is:
+     *
+     * <pre>metadata/contentInfo/featureTypes/featureTypeName</pre>
      *
      * @param  type         the feature type to add, or {@code null}.
      * @param  occurrences  number of instances of the given features, or {@code null} if unknown.
@@ -711,31 +710,31 @@ public class MetadataBuilder {
             if (occurrences != null) {
                 info.setFeatureInstanceCount(shared(occurrences));
             }
-            featureDescription().getFeatureTypeInfo().add(info);
+            addIfNotPresent(featureDescription().getFeatureTypeInfo(), info);
         }
     }
 
     /**
      * Adds the given coordinate reference system to metadata, if it does not already exists.
-     * This method ensures that there is no duplicated values. Comparisons ignore metadata.
+     * This method ensures that there is no duplicated values.
+     * Storage location is:
+     *
+     * <pre>metadata/referenceSystemInfo</pre>
      *
      * @param  crs  the coordinate reference system to add to the metadata, or {@code null} if none.
      */
     public final void add(final CoordinateReferenceSystem crs) {
         if (crs != null) {
-            final Collection<ReferenceSystem> systems = metadata().getReferenceSystemInfo();
-            for (final ReferenceSystem existing : systems) {
-                if (Utilities.equalsIgnoreMetadata(crs, existing)) {
-                    return;
-                }
-            }
-            systems.add(crs);
+            addIfNotPresent(metadata().getReferenceSystemInfo(), crs);
         }
     }
 
     /**
      * Adds the given envelope, including its CRS, to the metadata. If the metadata already contains a geographic
      * bounding box, then a new bounding box is added; this method does not compute the union of the two boxes.
+     * Storage location is:
+     *
+     * <pre>metadata/identificationInfo/extent/geographicElement</pre>
      *
      * @param  envelope  the extent to add in the metadata, or {@code null} if none.
      * @throws TransformException if an error occurred while converting the given envelope to extents.
@@ -760,6 +759,10 @@ public class MetadataBuilder {
      *   <li>{@code northBoundLatitude} (the maximal φ value), or {@code NaN}</li>
      * </ul>
      *
+     * Storage location is:
+     *
+     * <pre>metadata/identificationInfo/extent/geographicElement</pre>
+     *
      * @param  ordinates  the geographic coordinates.
      * @param  index      index of the first value to use in the given array.
      */
@@ -773,6 +776,9 @@ public class MetadataBuilder {
 
     /**
      * Adds a temporal extent covered by the data.
+     * Storage location is:
+     *
+     * <pre>metadata/identificationInfo/extent/temporalElement</pre>
      *
      * @param  startTime  when the data begins, or {@code null}.
      * @param  endTime    when the data ends, or {@code null}.
@@ -791,6 +797,9 @@ public class MetadataBuilder {
     /**
      * Adds a date of the given type. This is not the data acquisition time,
      * but rather the metadata creation or last update time.
+     * Storage location is:
+     *
+     * <pre>metadata/identificationInfo/citation/date/date</pre>
      *
      * @param date  the date to add, or {@code null}.
      * @param type  the type of the date to add, or {@code null}.
@@ -845,18 +854,21 @@ public class MetadataBuilder {
 
     /**
      * Adds a title or alternate title of the resource.
+     * Storage location is:
      *
-     * @param value  the resource title or alternate title, or {@code null} if none.
+     * <pre>metadata/identificationInfo/citation/title</pre>
+     *
+     * @param title  the resource title or alternate title, or {@code null} if none.
      */
-    public final void addTitle(final CharSequence value) {
-        final InternationalString i18n = trim(value);
+    public final void addTitle(final CharSequence title) {
+        final InternationalString i18n = trim(title);
         if (i18n != null) {
             final DefaultCitation citation = citation();
             final InternationalString current = citation.getTitle();
             if (current == null) {
                 citation.setTitle(i18n);
             } else if (!equals(current, i18n)) {
-                citation.getAlternateTitles().add(i18n);
+                addIfNotPresent(citation.getAlternateTitles(), i18n);
             }
         }
     }
@@ -864,6 +876,9 @@ public class MetadataBuilder {
     /**
      * Adds a brief narrative summary of the resource(s).
      * If a summary already existed, the new one will be appended after a new line.
+     * Storage location is:
+     *
+     * <pre>metadata/identificationInfo/abstract</pre>
      *
      * @param description  the summary of resource(s), or {@code null} if none.
      */
@@ -878,6 +893,9 @@ public class MetadataBuilder {
     /**
      * Adds an author name. If an author was already defined with a different name,
      * then a new party instance is created.
+     * Storage location is:
+     *
+     * <pre>metadata/identificationInfo/citation/party/name</pre>
      *
      * @param  name  the name of the author or publisher, or {@code null} if none.
      */
@@ -899,6 +917,9 @@ public class MetadataBuilder {
 
     /**
      * Adds recognition of those who contributed to the resource(s).
+     * Storage location is:
+     *
+     * <pre>metadata/identificationInfo/credit</pre>
      *
      * @param  credit  recognition of those who contributed to the resource(s).
      */
@@ -912,6 +933,9 @@ public class MetadataBuilder {
     /**
      * Adds a data identifier (not necessarily the same as the metadata identifier).
      * Empty strings (ignoring spaces) are ignored.
+     * Storage location is:
+     *
+     * <pre>metadata/identificationInfo/citation/identifier</pre>
      *
      * @param  code  the identifier code, or {@code null} if none.
      */
@@ -1127,6 +1151,10 @@ parse:      for (int i = 0; i < length;)
      *                     └─Role……………………………………… Owner
      * }
      *
+     * Storage location is:
+     *
+     * <pre>metadata/identificationInfo/resourceConstraint</pre>
+     *
      * @param  notice  the legal notice, or {@code null} if none.
      */
     public final void parseLegalNotice(final String notice) {
@@ -1138,6 +1166,9 @@ parse:      for (int i = 0; i < length;)
     /**
      * Adds a platform on which instrument are installed. If a platform was already defined
      * with a different identifier, then a new platform instance will be created.
+     * Storage location is:
+     *
+     * <pre>metadata/acquisitionInformation/platform/identifier</pre>
      *
      * @param  identifier  identifier of the platform to add, or {@code null}.
      */
@@ -1159,6 +1190,9 @@ parse:      for (int i = 0; i < length;)
 
     /**
      * Adds an instrument or sensor on the platform.
+     * Storage location is:
+     *
+     * <pre>metadata/acquisitionInformation/platform/instrument/identifier</pre>
      *
      * @param  identifier  identifier of the sensor to add, or {@code null}.
      */
@@ -1172,6 +1206,9 @@ parse:      for (int i = 0; i < length;)
 
     /**
      * Adds an event that describe the time at which data were acquired.
+     * Storage location is:
+     *
+     * <pre>metadata/acquisitionInformation/operation/significantEvent/time</pre>
      *
      * @param  time  the acquisition time, or {@code null}.
      *
@@ -1191,22 +1228,32 @@ parse:      for (int i = 0; i < length;)
     }
 
     /**
-     * Add a requirement into acquisition.
+     * Adds the identifier of the requirement to be satisfied by data acquisition.
+     * Storage location is:
      *
-     * @param  identifier  requirement identifier, or {@code null}.
+     * <pre>metadata/acquisitionInformation/acquisitionRequirement/identifier</pre>
+     *
+     * @param  identifier  unique name or code for the requirement, or {@code null}.
      */
     public final void addAcquisitionRequirement(String identifier) {
         if (identifier != null && !(identifier = identifier.trim()).isEmpty()) {
             final DefaultRequirement r = new DefaultRequirement();
             r.setIdentifier(new DefaultIdentifier(identifier));
-            acquisition().getAcquisitionRequirements().add(r);
+            addIfNotPresent(acquisition().getAcquisitionRequirements(), r);
         }
     }
 
     /**
-     * Add a Processing metadata object which describe data processing.
+     * Adds information about the procedure, process and algorithm applied in a process step.
+     * If a processing was already defined with a different identifier, then a new processing
+     * instance will be created. Storage location is:
+     *
+     * <pre>metadata/resourceLineage/processStep/processingInformation/identifier</pre>
      *
-     * @param  identifier  requirement identifier, or {@code null}.
+     * @param  identifier  identification of the processing package that produced the data, or {@code null}.
+     *
+     * @see #addSoftwareReference(CharSequence)
+     * @see #addHostComputer(CharSequence)
      */
     public final void addProcessing(String identifier) {
         if (identifier != null && !(identifier = identifier.trim()).isEmpty()) {
@@ -1217,7 +1264,7 @@ parse:      for (int i = 0; i < length;)
                         return;
                     }
                     processStep().setProcessingInformation(processing);
-                    lineage().getProcessSteps().add(processStep());
+                    addIfNotPresent(lineage().getProcessSteps(), processStep);
                     processing  = null;
                     processStep = null;
                 }
@@ -1227,39 +1274,54 @@ parse:      for (int i = 0; i < length;)
     }
 
     /**
-     * The computer and/or operating system in use at the processing time.
-     */
-    public final void addProcessingPlatform(String identifier) {
-        // TODO
-    }
-
-    /**
      * Adds a reference to document describing processing software.
+     * This is added to the processing identified by last call to {@link #addProcessing(String)}.
+     * Storage location is:
+     *
+     * <pre>metadata/resourceLineage/processStep/processingInformation/softwareReference/title</pre>
      *
-     * @param title  title of the document that describe the software, or {@code null} if none.
+     * @param  title  title of the document that describe the software, or {@code null} if none.
      */
-    public final void addSoftwareReferences(String title) {
-        if (title != null && !(title = title.trim()).isEmpty()) {
-            processing().getSoftwareReferences().add(new DefaultCitation(title));
+    public final void addSoftwareReference(final CharSequence title) {
+        final InternationalString i18n = trim(title);
+        if (i18n != null) {
+            addIfNotPresent(processing().getSoftwareReferences(), new DefaultCitation(i18n));
         }
     }
 
     /**
-     * Sets the additional details about the processing procedures.
+     * Adds information about the computer and/or operating system in use at the processing time.
+     * This is added to the processing identified by last call to {@link #addProcessing(String)}.
+     * Storage location is:
+     *
+     * <pre>metadata/resourceLineage/processStep/processingInformation/procedureDescription</pre>
      *
-     * @param procedureDescription additional details about the processing procedures, or {@code null}.
+     * @param  platform  name of the computer or operation system on which the processing has been executed.
      */
-    public final void setProcedureDescription(String procedureDescription) {
-        processing().setProcedureDescription(new DefaultInternationalString(procedureDescription));
+    public final void addHostComputer(final CharSequence platform) {
+        InternationalString i18n = trim(platform);
+        if (i18n != null) {
+            i18n = Resources.formatInternational(Resources.Keys.ProcessingExecutedOn_1, i18n);
+            final DefaultProcessing p = processing();
+            p.setProcedureDescription(append(p.getProcedureDescription(), i18n));
+        }
     }
 
     /**
-     * Sets the reference to documentation describing the processing.
+     * Adds additional details about the process step.
+     * If a description already exists, the new one will be added on a new line.
+     * Storage location is:
+     *
+     * <pre>metadata/resourceLineage/processStep/description</pre>
      *
-     * @param processingDocumentation documentation describing the processing, or {@code null}.
+     * @param  description  additional details about the process step, or {@code null}.
      */
-    public final void setProcessingDocumentation(CharSequence processingDocumentation) {
-        processing().getDocumentations().add(new DefaultCitation(processingDocumentation));
+    public final void addProcessDescription(final CharSequence description) {
+        final InternationalString i18n = trim(description);
+        if (i18n != null) {
+            final DefaultProcessStep ps = processStep();
+            ps.setDescription(append(ps.getDescription(), i18n));
+        }
     }
 
     /**
@@ -1267,7 +1329,10 @@ parse:      for (int i = 0; i < length;)
      * This method does nothing if the given value is {@link Double#NaN}.
      *
      * <p>This method is available only if {@link #commitCoverageDescription(boolean)}
-     * has been invoked with the {@code electromagnetic} parameter set to {@code true}.</p>
+     * has been invoked with the {@code electromagnetic} parameter set to {@code true}.
+     * Storage location is:</p>
+     *
+     * <pre>metadata/contentInfo/cloudCoverPercentage</pre>
      *
      * @param  value  the new cloud percentage.
      * @throws IllegalArgumentException if the given value is out of range.
@@ -1284,7 +1349,10 @@ parse:      for (int i = 0; i < length;)
      * This method does nothing if the given value is {@link Double#NaN}.
      *
      * <p>This method is available only if {@link #commitCoverageDescription(boolean)}
-     * has been invoked with the {@code electromagnetic} parameter set to {@code true}.</p>
+     * has been invoked with the {@code electromagnetic} parameter set to {@code true}.
+     * Storage location is:</p>
+     *
+     * <pre>metadata/contentInfo/illuminationAzimuthAngle</pre>
      *
      * @param  value  the new illumination azimuth angle, or {@code null}.
      * @throws IllegalArgumentException if the given value is out of range.
@@ -1302,7 +1370,10 @@ parse:      for (int i = 0; i < length;)
      * This method does nothing if the given value is {@link Double#NaN}.
      *
      * <p>This method is available only if {@link #commitCoverageDescription(boolean)}
-     * has been invoked with the {@code electromagnetic} parameter set to {@code true}.</p>
+     * has been invoked with the {@code electromagnetic} parameter set to {@code true}.
+     * Storage location is:</p>
+     *
+     * <pre>metadata/contentInfo/illuminationElevationAngle</pre>
      *
      * @param  value  the new illumination azimuth angle, or {@code null}.
      * @throws IllegalArgumentException if the given value is out of range.
@@ -1315,6 +1386,9 @@ parse:      for (int i = 0; i < length;)
 
     /**
      * Adds a linear resolution in metres.
+     * Storage location is:
+     *
+     * <pre>metadata/identificationInfo/spatialResolution/distance</pre>
      *
      * @param  distance  the resolution in metres, or {@code NaN} if none.
      */
@@ -1327,14 +1401,21 @@ parse:      for (int i = 0; i < length;)
     }
 
     /**
-     * Adds a new format. The given name should be a short name like "GeoTIFF".
+     * Sets the file format. The given name should be a short name like "GeoTIFF".
      * The long name will be inferred from the given short name, if possible.
+     * Storage location is:
+     *
+     * <pre>metadata/identificationInfo/resourceFormat/formatSpecificationCitation/alternateTitle</pre>
      *
      * @param  abbreviation  the format short name or abbreviation, or {@code null}.
+     * @throws MetadataStoreException  if this method can not connect to the {@code jdbc/SpatialMetadata} database.
+     *         Callers should generally handle this exception as a recoverable one (i.e. log a warning and continue).
      */
-    public final void addFormat(final CharSequence abbreviation) {
+    public final void setFormat(final String abbreviation) throws MetadataStoreException {
         if (abbreviation != null && abbreviation.length() != 0) {
-            addIfNotPresent(identification().getResourceFormats(), new DefaultFormat(abbreviation, null));
+            if (format == null) {
+                format = MetadataSource.getDefault().lookup(Format.class, abbreviation);
+            }
         }
     }
 
@@ -1354,6 +1435,9 @@ parse:      for (int i = 0; i < length;)
 
     /**
      * Sets the number of cells along the given dimension.
+     * Storage location is:
+     *
+     * <pre>metadata/spatialRepresentationInfo/axisDimensionProperties/dimensionName</pre>
      *
      * @param  dimension  the axis dimension, as a {@code short} for avoiding excessive values.
      * @param  name       the name to set for the given dimension.
@@ -1364,6 +1448,9 @@ parse:      for (int i = 0; i < length;)
 
     /**
      * Sets the number of cells along the given dimension.
+     * Storage location is:
+     *
+     * <pre>metadata/spatialRepresentationInfo/axisDimensionProperties/dimensionSize</pre>
      *
      * @param  dimension  the axis dimension, as a {@code short} for avoiding excessive values.
      * @param  length     number of cell values along the given dimension.
@@ -1375,6 +1462,9 @@ parse:      for (int i = 0; i < length;)
     /**
      * Adds a minimal value for the current sample dimension. If a minimal value was already defined, then
      * the new value will be set only if it is smaller than the existing one. {@code NaN} values are ignored.
+     * Storage location is:
+     *
+     * <pre>metadata/contentInfo/attributeGroup/attribute/minValue</pre>
      *
      * @param value  the minimal value to add to the existing range of sample values, or {@code NaN}.
      */
@@ -1391,6 +1481,9 @@ parse:      for (int i = 0; i < length;)
     /**
      * Adds a maximal value for the current sample dimension. If a maximal value was already defined, then
      * the new value will be set only if it is greater than the existing one. {@code NaN} values are ignored.
+     * Storage location is:
+     *
+     * <pre>metadata/contentInfo/attributeGroup/attribute/maxValue</pre>
      *
      * @param value  the maximal value to add to the existing range of sample values, or {@code NaN}.
      */
@@ -1406,6 +1499,9 @@ parse:      for (int i = 0; i < length;)
 
     /**
      * Adds a compression name.
+     * Storage location is:
+     *
+     * <pre>metadata/identificationInfo/resourceFormat/fileDecompressionTechnique</pre>
      *
      * @param value  the compression name, or {@code null}.
      */
@@ -1430,7 +1526,6 @@ parse:      for (int i = 0; i < length;)
         newGridRepresentation();
         newFeatureTypes();
         newCoverage(false);
-        newDistribution();
         newAcquisition();
         final DefaultMetadata md = metadata;
         metadata = null;

Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.java?rev=1770274&r1=1770273&r2=1770274&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.java [UTF-8] Thu Nov 17 20:14:36 2016
@@ -20,8 +20,10 @@ import java.net.URL;
 import java.util.Locale;
 import java.util.MissingResourceException;
 import javax.annotation.Generated;
+import org.opengis.util.InternationalString;
 import org.apache.sis.util.resources.KeyConstants;
 import org.apache.sis.util.resources.IndexedResourceBundle;
+import org.apache.sis.util.resources.ResourceInternationalString;
 
 
 /**
@@ -75,6 +77,11 @@ public final class Resources extends Ind
         public static final short MissingSchemeInURI_1 = 1;
 
         /**
+         * Processing executed on {0}.
+         */
+        public static final short ProcessingExecutedOn_1 = 5;
+
+        /**
          * Can not move backward in the “{0}” stream.
          */
         public static final short StreamIsForwardOnly_1 = 2;
@@ -177,4 +184,43 @@ public final class Resources extends Ind
     {
         return forLocale(null).getString(key, arg0, arg1, arg2);
     }
+
+    /**
+     * The international string to be returned by {@link formatInternational}.
+     */
+    private static final class International extends ResourceInternationalString {
+        private static final long serialVersionUID = -7265791441872360274L;
+
+        International(short key)                           {super(key);}
+        International(short key, Object args)              {super(key, args);}
+        @Override protected KeyConstants getKeyConstants() {return Keys.INSTANCE;}
+        @Override protected IndexedResourceBundle getBundle(final Locale locale) {
+            return forLocale(locale);
+        }
+    }
+
+    /**
+     * Gets an international string for the given key. This method does not check for the key
+     * validity. If the key is invalid, then a {@link MissingResourceException} may be thrown
+     * when a {@link InternationalString#toString(Locale)} method is invoked.
+     *
+     * @param  key  the key for the desired string.
+     * @return an international string for the given key.
+     */
+    public static InternationalString formatInternational(final short key) {
+        return new International(key);
+    }
+
+    /**
+     * Gets an international string for the given key. This method does not check for the key
+     * validity. If the key is invalid, then a {@link MissingResourceException} may be thrown
+     * when a {@link InternationalString#toString(Locale)} method is invoked.
+     *
+     * @param  key   the key for the desired string.
+     * @param  args  values to substitute to "{0}", "{1}", <i>etc</i>.
+     * @return an international string for the given key.
+     */
+    public static InternationalString formatInternational(final short key, final Object... args) {
+        return new International(key, args);
+    }
 }

Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.properties?rev=1770274&r1=1770273&r2=1770274&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.properties [ISO-8859-1] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.properties [ISO-8859-1] Thu Nov 17 20:14:36 2016
@@ -22,5 +22,6 @@
 ExcessiveStringSize_3             = Character string in the \u201c{0}\u201d file is too long. The string has {2} characters while the limit is {1}.
 InconsistentNameComponents_1      = Components of the \u201c{0}\u201d name are inconsistent with those of the name that was added.
 MissingSchemeInURI_1              = Missing scheme in \u201c{0}\u201d URI.
+ProcessingExecutedOn_1            = Processing executed on {0}.
 StreamIsForwardOnly_1             = Can not move backward in the \u201c{0}\u201d stream.
 UnknownFormatFor_1                = Format of \u201c{0}\u201d is not recognized.

Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties?rev=1770274&r1=1770273&r2=1770274&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties [ISO-8859-1] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties [ISO-8859-1] Thu Nov 17 20:14:36 2016
@@ -27,5 +27,6 @@
 ExcessiveStringSize_3             = La cha\u00eene de caract\u00e8res dans le fichier \u00ab\u202f{0}\u202f\u00bb est trop longue. La cha\u00eene fait {2} caract\u00e8res alors que la limite est {1}.
 InconsistentNameComponents_1      = Les \u00e9l\u00e9ments qui composent le nom \u00ab\u202f{0}\u202f\u00bb ne sont pas coh\u00e9rents avec ceux du nom qui avait \u00e9t\u00e9 ajout\u00e9.
 MissingSchemeInURI_1              = Il manque le sch\u00e9ma dans l\u2019URI \u00ab\u202f{0}\u202f\u00bb.
+ProcessingExecutedOn_1            = Traitement ex\u00e9cut\u00e9 sur {0}.
 StreamIsForwardOnly_1             = Ne peut pas reculer dans le flux de donn\u00e9es \u00ab\u202f{0}\u202f\u00bb.
 UnknownFormatFor_1                = Le format de \u00ab\u202f{0}\u202f\u00bb n\u2019est pas reconnu.

Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java?rev=1770274&r1=1770273&r2=1770274&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java [UTF-8] Thu Nov 17 20:14:36 2016
@@ -46,6 +46,7 @@ import org.apache.sis.internal.referenci
 import org.apache.sis.internal.storage.MetadataBuilder;
 import org.apache.sis.geometry.GeneralEnvelope;
 import org.apache.sis.metadata.iso.DefaultMetadata;
+import org.apache.sis.metadata.sql.MetadataStoreException;
 import org.apache.sis.storage.DataStore;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.DataStoreContentException;
@@ -485,8 +486,12 @@ public final class Store extends DataSto
     public synchronized Metadata getMetadata() throws DataStoreException {
         if (metadata == null) {
             final MetadataBuilder builder = new MetadataBuilder();
+            try {
+                builder.setFormat(timeEncoding != null && hasTrajectories ? "CSV/MF" : "CSV");
+            } catch (MetadataStoreException e) {
+                listeners.warning(null, e);
+            }
             builder.add(encoding);
-            builder.addFormat(timeEncoding != null && hasTrajectories ? "CSV/MF" : "CSV");
             builder.add(ScopeCode.DATASET);
             try {
                 builder.addExtent(envelope);



Mime
View raw message