Coverage Report - org.geotoolkit.image.io.metadata.SpatialMetadataFormat
 
Classes in this File Line Coverage Branch Coverage Complexity
SpatialMetadataFormat
80 %
146/182
78 %
87/111
3,032
SpatialMetadataFormat$Geotk
88 %
8/9
N/A
3,032
SpatialMetadataFormat$ISO
0 %
0/5
N/A
3,032
 
 1  
 /*
 2  
  *    Geotoolkit.org - An Open Source Java GIS Toolkit
 3  
  *    http://www.geotoolkit.org
 4  
  *
 5  
  *    (C) 2009-2012, Open Source Geospatial Foundation (OSGeo)
 6  
  *    (C) 2009-2012, Geomatys
 7  
  *
 8  
  *    This library is free software; you can redistribute it and/or
 9  
  *    modify it under the terms of the GNU Lesser General Public
 10  
  *    License as published by the Free Software Foundation;
 11  
  *    version 2.1 of the License.
 12  
  *
 13  
  *    This library is distributed in the hope that it will be useful,
 14  
  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 15  
  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 16  
  *    Lesser General Public License for more details.
 17  
  */
 18  
 package org.geotoolkit.image.io.metadata;
 19  
 
 20  
 import java.util.Locale;
 21  
 import java.util.Arrays;
 22  
 import java.util.Map;
 23  
 import java.util.List;
 24  
 import java.util.HashMap;
 25  
 import java.util.Collections;
 26  
 import javax.imageio.ImageTypeSpecifier;
 27  
 import javax.imageio.metadata.IIOMetadataFormat;
 28  
 import javax.imageio.metadata.IIOMetadataFormatImpl;
 29  
 
 30  
 import org.opengis.util.CodeList;
 31  
 import org.opengis.metadata.Identifier;
 32  
 import org.opengis.metadata.Metadata;
 33  
 import org.opengis.metadata.acquisition.AcquisitionInformation;
 34  
 import org.opengis.metadata.acquisition.EnvironmentalRecord;
 35  
 import org.opengis.metadata.acquisition.Instrument;
 36  
 import org.opengis.metadata.acquisition.Objective;
 37  
 import org.opengis.metadata.acquisition.Platform;
 38  
 import org.opengis.metadata.citation.Citation;
 39  
 import org.opengis.metadata.content.Band;
 40  
 import org.opengis.metadata.content.ImageDescription;
 41  
 import org.opengis.metadata.content.RangeDimension;
 42  
 import org.opengis.metadata.content.RangeElementDescription;
 43  
 import org.opengis.metadata.identification.DataIdentification;
 44  
 import org.opengis.metadata.identification.Identification;
 45  
 import org.opengis.metadata.identification.Keywords;
 46  
 import org.opengis.metadata.identification.Resolution;
 47  
 import org.opengis.metadata.extent.Extent;
 48  
 import org.opengis.metadata.extent.GeographicBoundingBox;
 49  
 import org.opengis.metadata.extent.VerticalExtent;
 50  
 import org.opengis.metadata.spatial.Georectified;
 51  
 import org.opengis.metadata.quality.Element;
 52  
 import org.opengis.metadata.quality.DataQuality;
 53  
 import org.opengis.parameter.ParameterValue;
 54  
 import org.opengis.parameter.ParameterValueGroup;
 55  
 import org.opengis.referencing.IdentifiedObject;
 56  
 import org.opengis.referencing.cs.*;
 57  
 import org.opengis.referencing.crs.*;
 58  
 import org.opengis.referencing.datum.*;
 59  
 import org.opengis.referencing.operation.Conversion;
 60  
 import org.opengis.coverage.grid.GridEnvelope;
 61  
 import org.opengis.coverage.grid.RectifiedGrid;
 62  
 
 63  
 import org.geotoolkit.util.NumberRange;
 64  
 import org.geotoolkit.metadata.KeyNamePolicy;
 65  
 import org.geotoolkit.metadata.MetadataStandard;
 66  
 import org.geotoolkit.referencing.cs.*;
 67  
 import org.geotoolkit.referencing.crs.*;
 68  
 import org.geotoolkit.referencing.datum.*;
 69  
 import org.geotoolkit.gui.swing.tree.Trees;
 70  
 import org.geotoolkit.gui.swing.tree.TreeTableNode;
 71  
 import org.geotoolkit.internal.image.io.DataTypes;
 72  
 import org.geotoolkit.resources.Errors;
 73  
 
 74  
 import static org.geotoolkit.util.ArgumentChecks.ensureNonNull;
 75  
 
 76  
 
 77  
 /**
 78  
  * Describes the structure of {@linkplain SpatialMetadata spatial metadata}.
 79  
  * The default {@linkplain #getStreamInstance(String) stream} and {@link #getImageInstance(String)
 80  
  * image} formats are inferred from a subset of the GeoAPI metadata interfaces, especially
 81  
  * {@link Metadata} and {@link ImageDescription}. Consequently those instances can be considered
 82  
  * as profiles of ISO 19115-2, with a few minor departures:
 83  
  * <p>
 84  
  * <ul>
 85  
  *   <li>The {@link Band} interface defined by ISO 19115-2 is used only when the values are
 86  
  *       measurements of wavelengths in the electromagnetic spectrum, as specified in the ISO
 87  
  *       specification. Otherwise the {@link SampleDimension} interface (which is very similar)
 88  
  *       is used.</li>
 89  
  * </ul>
 90  
  * <p>
 91  
  * <a name="default-formats">The tree structures are show below</a>. As a general rule, the name of
 92  
  * <em>elements</em> start with a upper case letter while the name of <em>attributes</em> start with
 93  
  * a lower case letter. The valid types of attributes values are <a href="package-summary.html#accessor-types">listed here</a>.
 94  
  * For browsing these trees in an applet together with additional information, see the
 95  
  * <a href="http://www.geotoolkit.org/demos/geotk-simples/applet/IIOMetadataPanel.html">IIOMetadataPanel applet</a>.
 96  
  *
 97  
 <blockquote><table border="1" cellpadding="12">
 98  
 <tr bgcolor="lightblue"><th>Stream metadata</th><th>Image metadata</th></tr>
 99  
 <tr><td nowrap valign="top" width="50%">
 100  
 <pre>geotk-coverageio_3.07
 101  
 ├───<b>DiscoveryMetadata</b> : {@linkplain DataIdentification}
 102  
 │   ├───citation
 103  
 │   ├───abstract
 104  
 │   ├───purpose
 105  
 │   ├───credits
 106  
 │   ├───status
 107  
 │   ├───<b>DescriptiveKeywords</b> : {@linkplain Keywords}[]
 108  
 │   │   └───DescriptiveKeywordsEntry
 109  
 │   │       ├───keywords
 110  
 │   │       ├───thesaurusName
 111  
 │   │       └───type
 112  
 │   ├───<b>SpatialResolution</b> : {@linkplain Resolution}
 113  
 │   │   ├───distance
 114  
 │   │   └───EquivalentScale
 115  
 │   │       └───denominator
 116  
 │   ├───topicCategories
 117  
 │   ├───environmentDescription
 118  
 │   ├───<b>Extent</b> : {@linkplain Extent}
 119  
 │   │   ├───description
 120  
 │   │   ├───<b>GeographicElement</b> : {@linkplain GeographicBoundingBox}
 121  
 │   │   │   ├───inclusion
 122  
 │   │   │   ├───westBoundLongitude
 123  
 │   │   │   ├───eastBoundLongitude
 124  
 │   │   │   ├───southBoundLatitude
 125  
 │   │   │   └───northBoundLatitude
 126  
 │   │   └───<b>VerticalElement</b> : {@linkplain VerticalExtent}
 127  
 │   │       ├───minimumValue
 128  
 │   │       ├───maximumValue
 129  
 │   │       └───verticalCRS
 130  
 │   └───supplementalInformation
 131  
 ├───<b>AcquisitionMetadata</b> : {@linkplain AcquisitionInformation}
 132  
 │   ├───<b>EnvironmentalConditions</b> : {@linkplain EnvironmentalRecord}
 133  
 │   │   ├───averageAirTemperature
 134  
 │   │   ├───maxRelativeHumidity
 135  
 │   │   ├───maxAltitude
 136  
 │   │   └───meteorologicalConditions
 137  
 │   └───<b>Platform</b> : {@linkplain Platform}
 138  
 │       ├───citation
 139  
 │       ├───identifier
 140  
 │       ├───description
 141  
 │       └───Instruments
 142  
 │           └───<b>Instrument</b> : {@linkplain Instrument}
 143  
 │               ├───citation
 144  
 │               ├───Identifier : {@linkplain Identifier}
 145  
 │               │   ├───code
 146  
 │               │   └───authority
 147  
 │               ├───type
 148  
 │               └───description
 149  
 └───<b>QualityMetadata</b> : {@linkplain DataQuality}
 150  
     └───<b>Report</b> : {@linkplain Element}
 151  
         ├───namesOfMeasure
 152  
         ├───measureIdentification
 153  
         ├───measureDescription
 154  
         ├───evaluationMethodType
 155  
         ├───evaluationMethodDescription
 156  
         ├───evaluationProcedure
 157  
         └───date</pre>
 158  
 </td><td nowrap valign="top" width="50%">
 159  
 <pre>geotk-coverageio_3.07
 160  
 ├───<b>ImageDescription</b> : {@linkplain ImageDescription}
 161  
 │   ├───contentType
 162  
 │   ├───illuminationElevationAngle
 163  
 │   ├───illuminationAzimuthAngle
 164  
 │   ├───imagingCondition
 165  
 │   ├───ImageQualityCode : {@linkplain Identifier}
 166  
 │   │   ├───code
 167  
 │   │   └───authority
 168  
 │   ├───cloudCoverPercentage
 169  
 │   ├───ProcessingLevelCode : {@linkplain Identifier}
 170  
 │   │   ├───code
 171  
 │   │   └───authority
 172  
 │   ├───compressionGenerationQuantity
 173  
 │   ├───triangulationIndicator
 174  
 │   ├───radiometricCalibrationDataAvailable
 175  
 │   ├───cameraCalibrationInformationAvailable
 176  
 │   ├───filmDistortionInformationAvailable
 177  
 │   ├───lensDistortionInformationAvailable
 178  
 │   ├───<b>Dimensions</b> : {@linkplain SampleDimension}[]
 179  
 │   │   └───Dimension
 180  
 │   │       ├───descriptor
 181  
 │   │       ├───sequenceIdentifier
 182  
 │   │       ├───validSampleValues
 183  
 │   │       ├───fillSampleValues
 184  
 │   │       ├───minValue
 185  
 │   │       ├───maxValue
 186  
 │   │       ├───units
 187  
 │   │       ├───peakResponse
 188  
 │   │       ├───bitsPerValue
 189  
 │   │       ├───toneGradation
 190  
 │   │       ├───scaleFactor
 191  
 │   │       ├───offset
 192  
 │   │       ├───bandBoundaryDefinition
 193  
 │   │       ├───nominalSpatialResolution
 194  
 │   │       ├───transferFunctionType
 195  
 │   │       ├───transmittedPolarization
 196  
 │   │       └───detectedPolarization
 197  
 │   └───<b>RangeElementDescriptions</b> : {@linkplain RangeElementDescription}
 198  
 │       └───RangeElementDescription
 199  
 │           ├───name
 200  
 │           ├───definition
 201  
 │           └───rangeElements
 202  
 ├───<b>SpatialRepresentation</b> : {@linkplain Georectified}
 203  
 │   ├───numberOfDimensions
 204  
 │   ├───cellGeometry
 205  
 │   ├───centerPoint
 206  
 │   └───pointInPixel
 207  
 └───<b>RectifiedGridDomain</b> : {@linkplain RectifiedGrid}
 208  
     ├───<b>Limits</b> : {@linkplain GridEnvelope}
 209  
     │   ├───low
 210  
     │   └───high
 211  
     ├───origin
 212  
     ├───<b>OffsetVectors</b>
 213  
     │   └───OffsetVector
 214  
     │       └───values
 215  
     └───<b>CoordinateReferenceSystem</b> : {@linkplain CoordinateReferenceSystem}
 216  
         ├───name
 217  
         ├───type
 218  
         ├───<b>CoordinateSystem</b> : {@linkplain CoordinateSystem}
 219  
         │   ├───name
 220  
         │   ├───type
 221  
         │   ├───dimension
 222  
         │   └───Axes
 223  
         │       └───<b>CoordinateSystemAxis</b> : {@linkplain CoordinateSystemAxis}
 224  
         │           ├───name
 225  
         │           ├───direction
 226  
         │           ├───minimumValue
 227  
         │           ├───maximumValue
 228  
         │           ├───rangeMeaning
 229  
         │           └───unit
 230  
         ├───<b>Datum</b> : {@linkplain Datum}
 231  
         │   ├───name
 232  
         │   ├───type
 233  
         │   ├───<b>Ellipsoid</b> : {@linkplain Ellipsoid}
 234  
         │   │   ├───name
 235  
         │   │   ├───axisAbbrev
 236  
         │   │   ├───axisUnit
 237  
         │   │   ├───semiMajorAxis
 238  
         │   │   ├───semiMinorAxis
 239  
         │   │   └───inverseFlattening
 240  
         │   └───<b>PrimeMeridian</b> : {@linkplain PrimeMeridian}
 241  
         │       ├───name
 242  
         │       ├───greenwichLongitude
 243  
         │       └───angularUnit
 244  
         └───<b>Conversion</b> : {@linkplain Conversion}
 245  
             ├───name
 246  
             ├───method
 247  
             └───Parameters : {@linkplain ParameterValueGroup}
 248  
                 └───ParameterValue : {@linkplain ParameterValue}
 249  
                     ├───name
 250  
                     └───value</pre>
 251  
 </tr></table></blockquote>
 252  
  *
 253  
  * @author Martin Desruisseaux (Geomatys)
 254  
  * @version 3.20
 255  
  *
 256  
  * @see SpatialMetadata
 257  
  *
 258  
  * @since 3.04 (derived from 2.4)
 259  
  * @module
 260  
  */
 261  
 public class SpatialMetadataFormat extends IIOMetadataFormatImpl {
 262  
     /**
 263  
      * The metadata format name, which is {@value}. The {@link javax.imageio.metadata} package
 264  
      * description requires that we provide a version number as part of the format name. The
 265  
      * version number provided in this constant is set to the last Geotk version when this
 266  
      * format has been modified, and may change in any future version.
 267  
      */
 268  
     public static final String FORMAT_NAME = "geotk-coverageio_3.07";
 269  
 
 270  
     /**
 271  
      * The ISO-19115 format name, which is {@value}. This metadata format is big and supported
 272  
      * only by a few plugins like {@link org.geotoolkit.image.io.plugin.NetcdfImageReader}.
 273  
      * For applications that don't need to full verbosity of ISO 19115, consider using the
 274  
      * {@linkplain #getStreamInstance(String) stream metadata instance} identified by the
 275  
      * {@link #FORMAT_NAME} name instead.
 276  
      *
 277  
      * @since 3.20
 278  
      */
 279  
     public static final String ISO_FORMAT_NAME = "ISO-19115";
 280  
 
 281  
     /**
 282  
      * The policy for the names of the nodes to be inferred from the ISO objects.
 283  
      * We use JavaBeans names instead of UML identifiers in order to get the plural
 284  
      * form for collections.
 285  
      */
 286  1
     static final KeyNamePolicy NAME_POLICY = KeyNamePolicy.JAVABEANS_PROPERTY;
 287  
 
 288  
     /**
 289  
      * Holder for the default instance for <cite>ISO 19115</cite> metadata format.
 290  
      * Applies the <cite>Initialization-on-demand holder</cite> idiom, because the
 291  
      * ISO metadata format is very large and only occasionally used.
 292  
      */
 293  
     private static final class ISO {
 294  0
         private ISO() {}
 295  
 
 296  
         /** The ISO-19115 instance for <cite>stream</cite> metadata format. */
 297  
         static final SpatialMetadataFormat INSTANCE;
 298  
         static {
 299  0
             final SpatialMetadataFormatBuilder builder = new SpatialMetadataFormatBuilder(ISO_FORMAT_NAME);
 300  0
             builder.addTreeForISO19115(null);
 301  0
             INSTANCE = builder.build();
 302  0
         }
 303  
     }
 304  
 
 305  
     /**
 306  
      * Holder for the default instances of Geotk metadata format.
 307  
      * Applies the <cite>Initialization-on-demand holder</cite> idiom,
 308  
      * because the builder class is relatively large.
 309  
      */
 310  
     private static final class Geotk {
 311  0
         private Geotk() {}
 312  
 
 313  
         /** The default instance for <cite>stream</cite> metadata format. */
 314  
         static final SpatialMetadataFormat STREAM;
 315  
         static {
 316  1
             SpatialMetadataFormatBuilder builder = new SpatialMetadataFormatBuilder(FORMAT_NAME);
 317  1
             builder.addTreeForStream(null);
 318  1
             STREAM = builder.build();
 319  
         }
 320  
 
 321  
         /** The default instance for <cite>image</cite> metadata format. */
 322  
         static final SpatialMetadataFormat IMAGE;
 323  
         static {
 324  1
             final SpatialMetadataFormatBuilder builder = new SpatialMetadataFormatBuilder(FORMAT_NAME);
 325  1
             builder.addTreeForImage(null);
 326  1
             builder.addTreeForCRS("RectifiedGridDomain");
 327  1
             IMAGE = builder.build();
 328  1
         }
 329  
     }
 330  
 
 331  
     /**
 332  
      * The default instance for <cite>stream</cite> metadata format. This is the metadata
 333  
      * format that apply to file as a whole, which may contain more than one image. The
 334  
      * tree structure is documented in the <a href="#default-formats">class javadoc</a>.
 335  
      *
 336  
      * @since 3.05
 337  
      *
 338  
      * @deprecated Replaced by call to {@code getStreamInstance(FORMAT_NAME)}.
 339  
      */
 340  
     @Deprecated
 341  1
     public static final SpatialMetadataFormat STREAM = Geotk.STREAM;
 342  
 
 343  
     /**
 344  
      * The default instance for <cite>image</cite> metadata format. This
 345  
      * is the metadata format that apply to a particular image in a file.
 346  
      * The tree structure is documented in the <a href="#default-formats">class javadoc</a>.
 347  
      *
 348  
      * @since 3.05
 349  
      *
 350  
      * @deprecated Replaced by call to {@code getImageInstance(FORMAT_NAME)}.
 351  
      */
 352  
     @Deprecated
 353  1
     public static final SpatialMetadataFormat IMAGE = Geotk.IMAGE;
 354  
 
 355  
     /**
 356  
      * Returns the <cite>stream</cite> metadata format for the given name. This is the metadata
 357  
      * format that apply to a file as a whole, which may contain more than one image. The
 358  
      * tree structure is documented in the <a href="#default-formats">class javadoc</a>.
 359  
      *
 360  
      * @param  name The {@link #FORMAT_NAME} or {@link #ISO_FORMAT_NAME} constant, or {@code null}
 361  
      *              for the default format (currently {@value #FORMAT_NAME}).
 362  
      * @return The stream metadata format for the given name.
 363  
      * @throws IllegalArgumentException If the given name is not one of the supported constants.
 364  
      *
 365  
      * @since 3.20
 366  
      */
 367  
     public static SpatialMetadataFormat getStreamInstance(final String name) {
 368  16
         if (name != null) {
 369  0
             if (name.equals(ISO_FORMAT_NAME)) {
 370  0
                 return ISO.INSTANCE;
 371  
             }
 372  0
             if (!name.equals(FORMAT_NAME)) {
 373  0
                 throw new IllegalArgumentException(Errors.format(
 374  
                         Errors.Keys.ILLEGAL_ARGUMENT_$2, "name", name));
 375  
             }
 376  
         }
 377  16
         return Geotk.STREAM;
 378  
     }
 379  
 
 380  
     /**
 381  
      * Returns the <cite>image</cite> metadata format for the given name.
 382  
      * This is the metadata format that apply to a particular image in a file.
 383  
      * The tree structure is documented in the <a href="#default-formats">class javadoc</a>.
 384  
      *
 385  
      * @param  name The {@link #FORMAT_NAME} constant, or {@code null}
 386  
      *              for the default format (currently {@value #FORMAT_NAME}).
 387  
      * @return The image metadata format for the given name.
 388  
      * @throws IllegalArgumentException If the given name is not one of the supported constants.
 389  
      *
 390  
      * @since 3.20
 391  
      */
 392  
     public static SpatialMetadataFormat getImageInstance(final String name) {
 393  402
         if (name != null) {
 394  
             // More formats may be added later (e.g. GML in JPEG2000).
 395  0
             if (!name.equals(FORMAT_NAME)) {
 396  0
                 throw new IllegalArgumentException(Errors.format(
 397  
                         Errors.Keys.ILLEGAL_ARGUMENT_$2, "name", name));
 398  
             }
 399  
         }
 400  402
         return Geotk.IMAGE;
 401  
     }
 402  
 
 403  
     /**
 404  
      * The metadata standards represented by each node. The most common standard is
 405  
      * {@link MetadataStandard#ISO_19115 ISO_19115}. This information is used for
 406  
      * {@linkplain #getDescription(String, String, Locale)} implementation.
 407  
      */
 408  2
     private final Map<String,MetadataStandard> standards = new HashMap<String,MetadataStandard>();
 409  
 
 410  
     /**
 411  
      * The mapping from method names to attribute or child element names for a given element.
 412  
      */
 413  2
     private final Map<String, Map<String,String>> namesMapping = new HashMap<String, Map<String,String>>();
 414  
 
 415  
     /**
 416  
      * The last value returned by {@link #getDescription(String, String, Locale)}, cached on
 417  
      * the assumption that the description of different attributes of the same element are
 418  
      * likely to be asked a few consecutive time.
 419  
      */
 420  
     private transient volatile MetadataDescriptions descriptions;
 421  
 
 422  
     /**
 423  
      * Creates an initially empty format. Subclasses shall invoke the various
 424  
      * {@code addFoo(...)} methods defined in this class or parent class for
 425  
      * adding new elements and attributes.
 426  
      *
 427  
      * @param rootName the name of the root element.
 428  
      */
 429  
     protected SpatialMetadataFormat(final String rootName) {
 430  2
         super(rootName, CHILD_POLICY_SOME);
 431  2
     }
 432  
 
 433  
     /**
 434  
      * Adds a new element or attribute of the given type as a child of the root. This method
 435  
      * performs the same work than {@link #addTree(MetadataStandard, Class, String, String, Map)},
 436  
      * except that the element is added at the root and the name is inferred from the given type
 437  
      * for convenience.
 438  
      *
 439  
      * @param standard     The metadata standard of the element or attribute to be added.
 440  
      * @param type         The type of the element or attribute to be added.
 441  
      * @param substitution The map of children types to substitute by other types, or {@code null}.
 442  
      *
 443  
      * @deprecated Replaced by {@link SpatialMetadataFormatBuilder}.
 444  
      */
 445  
     @Deprecated
 446  
     protected void addTree(final MetadataStandard standard, final Class<?> type,
 447  
             final Map<Class<?>,Class<?>> substitution)
 448  
     {
 449  0
         ensureNonNull("standard", standard);
 450  0
         ensureNonNull("type",     type);
 451  0
         addTree(standard, type, type.getSimpleName(), getRootName(), substitution);
 452  0
     }
 453  
 
 454  
     /**
 455  
      * Adds a new element or attribute of the given type and name as a child of the given node. If
 456  
      * the given type is a metadata, then that child is {@linkplain #addElement(String,String,int)
 457  
      * added as an element} and all its children are added recursively. Otherwise the type is
 458  
      * {@linkplain #addAttribute(String,String,int,boolean,String) added as an attribute}.
 459  
      *
 460  
      * {@section Element type}
 461  
      * This method expects a {@code type} argument, which can be a {@link CodeList} subclass,
 462  
      * one of the interfaces member of the given metadata {@code standard}, or a simple JSE
 463  
      * type (boolean, number of {@link String}). Do <strong>not</strong> specify collection
 464  
      * types, since the type of collection elements can not be inferred easily. To specify
 465  
      * a multi-occurrence, use the array type instead (e.g. {@code CoordinateSystemAxis[].class}).
 466  
      *
 467  
      * {@section Substitution map}
 468  
      * This method can be given an optional <cite>substitution map</cite>. If this map is non
 469  
      * null, then every occurrence of a class in the set of keys is replaced by the associated
 470  
      * class in the collection of values. The purpose of this map is to:
 471  
      *
 472  
      * <ul>
 473  
      *   <li><p>Replace a base class by some specialized subclass. Since {@code IIOMetadata} is
 474  
      *   about grided data (not generic {@code Feature}s), the exact subtype is often known at
 475  
      *   compile time, and we want the additional attributes to be declared unconditionally.
 476  
      *   Example:</p>
 477  
      *
 478  
      * <blockquote><pre>substitution.put({@linkplain RangeDimension}.class, {@linkplain Band}.class);</pre></blockquote></li>
 479  
      *
 480  
      *   <li><p>Exclude a particular class by setting the replacement to {@code null}. This is used
 481  
      *   for excluding large tree of metadata which may not be applicable. Example:</p>
 482  
      *
 483  
      * <blockquote><pre>substitution.put({@linkplain Objective}.class, null);</pre></blockquote></li>
 484  
      *
 485  
      *   <li><p>Replace an element class (including the whole tree behind it) by a single attribute.
 486  
      *   This simplification is especially useful for {@code Citation} because they typically appear
 487  
      *   in many different places with the same name ("<cite>citation</cite>"), while Image I/O does
 488  
      *   not allow many elements to have the same name (actually this is not strictly forbidden, but
 489  
      *   the getter methods return information only about the first occurrence of a given name).
 490  
      *   Converting an element to an attribute allow it to appear with the same name under different
 491  
      *   nodes, and can make the tree considerably simpler (at the cost of losing all the sub-tree
 492  
      *   below the converted element). Example:</p>
 493  
      *
 494  
      * <blockquote><pre>substitution.put({@linkplain Citation}.class, String.class);</pre></blockquote></li>
 495  
      *
 496  
      *   <li><p>Replace a collection by a singleton, by setting the source type to an array and the
 497  
      *   target type to the element of that array. This is useful when a collection seems an overkill
 498  
      *   for the specific case of stream or image metadata. Example:</p>
 499  
      *
 500  
      * <blockquote><pre>substitution.put({@linkplain Identification}[].class, {@linkplain Identification}.class);</pre></blockquote></li>
 501  
      * </ul>
 502  
      *
 503  
      * The substitution map applies only to children (if any), not to the type given directly to this
 504  
      * method.
 505  
      *
 506  
      * @param standard      The metadata standard of the element or attribute to be added.
 507  
      * @param type          The type of the element or attribute to be added (see javadoc).
 508  
      * @param elementName   The name of the element or attribute node to be added.
 509  
      * @param parentName    The name of the parent node to where to add the child.
 510  
      * @param substitution  The map of children types to substitute by other types (see javadoc),
 511  
      *                      or {@code null} if none.
 512  
      *
 513  
      * @deprecated Replaced by {@link SpatialMetadataFormatBuilder}.
 514  
      */
 515  
     @Deprecated
 516  
     protected void addTree(final MetadataStandard standard, final Class<?> type,
 517  
             final String elementName, final String parentName,
 518  
             final Map<Class<?>,Class<?>> substitution)
 519  
     {
 520  0
         ensureNonNull("standard",    standard);
 521  0
         ensureNonNull("type",        type);
 522  0
         ensureNonNull("elementName", elementName);
 523  0
         ensureNonNull("parentName",  parentName);
 524  0
         final SpatialMetadataFormatBuilder builder = new SpatialMetadataFormatBuilder(this);
 525  0
         if (substitution != null) {
 526  0
             builder.substitutions().putAll(substitution);
 527  
         }
 528  0
         builder.addTree(standard, type, elementName, parentName, false);
 529  0
     }
 530  
 
 531  
     /**
 532  
      * Adds a new element type to this metadata document format with a
 533  
      * child policy of {@link #CHILD_POLICY_REPEAT CHILD_POLICY_REPEAT}.
 534  
      * <p>
 535  
      * This method is defined mostly in order to give access to protected methods
 536  
      * from {@link SpatialMetadataFormatBuilder}.
 537  
      *
 538  
      * @param <T>           The compile-time type of the {@code type} argument.
 539  
      * @param standard      The standard from which the new element is derived, or {@code null}.
 540  
      * @param type          The legal class of the object value, or {@code null}.
 541  
      * @param elementName   The name of the new element.
 542  
      * @param parentName    The name of the element that will be the parent of the new element.
 543  
      * @param childPolicy   One of the {@code CHILD_POLICY_*} constants indicating the child policy
 544  
      *                      of the new element.
 545  
      * @param minOccurrence The minimum number of children of the node.
 546  
      * @param maxOccurrence The maximum number of children of the node.
 547  
      */
 548  
     @SuppressWarnings("fallthrough")
 549  
     final <T> void addElement(final MetadataStandard standard, final Class<T> type,
 550  
             final String elementName, final String parentName, int childPolicy,
 551  
             final int minOccurrence, final int maxOccurrence)
 552  
     {
 553  36
         switch (maxOccurrence) {
 554  1
             case 0:  childPolicy = CHILD_POLICY_EMPTY; // Fallthrough
 555  30
             case 1:  addElement(elementName, parentName, childPolicy); break;
 556  6
             default: addElement(elementName, parentName, minOccurrence, maxOccurrence); break;
 557  
         }
 558  36
         if (standard != null) {
 559  34
             standards.put(elementName, standard);
 560  
         }
 561  36
         if (type != null) {
 562  31
             addObjectValue(elementName, type);
 563  31
             addCustomAttributes(elementName, type);
 564  
         }
 565  36
     }
 566  
 
 567  
     /**
 568  
      * Adds a reference to an existing child element. This method is defined here only in
 569  
      * order to give access to the protected method from {@link SpatialMetadataFormatBuilder}.
 570  
      * This method is defined mostly in order to give access to a protected method from
 571  
      * {@link SpatialMetadataFormatBuilder}.
 572  
      */
 573  
     final void addExistingElement(final String elementName, final String parentName) {
 574  0
         addChildElement(elementName, parentName);
 575  0
     }
 576  
 
 577  
     /**
 578  
      * Allows an {@code Object} reference of a given class type to be stored in nodes implementing
 579  
      * the named element. This method delegates to one of the {@link #addObjectValue(String, Class,
 580  
      * boolean, Object) addObjectValue} methods defined in the super-class. The current
 581  
      * implementation is as below:
 582  
      *
 583  
      * {@preformat java
 584  
      *     addObjectValue(elementName, classType, mandatory, getDefaultValue(classType));
 585  
      * }
 586  
      *
 587  
      * @param <T> The compile-time type of {@code classType}.
 588  
      * @param elementName The name of the element for which to add an object value.
 589  
      * @param classType The legal class type of the object value.
 590  
      */
 591  
     final <T> void addObjectValue(final String elementName, final Class<T> classType) {
 592  34
         addObjectValue(elementName, classType, false, getDefaultValue(classType));
 593  34
     }
 594  
 
 595  
     /**
 596  
      * Adds a wrapper for a list of attributes. This is usually not needed, since attributes
 597  
      * can be declared with the {@link #VALUE_LIST VALUE_LIST} type. However in the case of
 598  
      * two-dimensional arrays (or lists of lists), the second dimension needs to be represented
 599  
      * by a wrapper element. The main use case if the list of offset vectors in a
 600  
      * {@linkplain RectifiedGrid rectified grid domain}, which can be represented as below:
 601  
      *
 602  
      * {@preformat text
 603  
      *     RectifiedGridDomain  : RectifiedGrid
 604  
      *     └───OffsetVectors    : List<double[]>
 605  
      *         └───OffsetVector : double[]
 606  
      *             └───values
 607  
      * }
 608  
      *
 609  
      * In the above example, the name of the {@code RectifiedGridDomain}, {@code OffsetVectors}
 610  
      * and {@code OffsetVector} nodes shall be specified by the {@code parentName},
 611  
      * {@code elementName} and {@code componentName} arguments respectively. The creation of
 612  
      * the {@code values} attribute is caller responsibility.
 613  
      *
 614  
      * @see #addElement(String, String, int, int)
 615  
      * @see #addElement(String, String, int)
 616  
      * @see #addObjectValue(String, Class, int, int)
 617  
      */
 618  
     final void addListWrapper(final MetadataStandard standard,
 619  
             final String parentName, final String elementName, final String componentName,
 620  
             final Class<?> componentType, final int minOccurrence, final int maxOccurrence)
 621  
     {
 622  1
         addElement(elementName, parentName, minOccurrence, maxOccurrence);
 623  1
         standards.put(elementName, standard);
 624  
 
 625  
         // The repeated element with no child, only a single attribute.
 626  1
         addElement(componentName, elementName, CHILD_POLICY_EMPTY);
 627  1
         addObjectValue(componentName, componentType, 0, Integer.MAX_VALUE);
 628  1
         standards.put(componentName, standard);
 629  1
     }
 630  
 
 631  
     /**
 632  
      * Adds an attribute for a code list or an enumeration. The attribute has no
 633  
      * default value and its type is {@link #DATATYPE_STRING DATATYPE_STRING}.
 634  
      * <p>
 635  
      * This method is defined mostly in order to give access to protected methods
 636  
      * from {@link SpatialMetadataFormatBuilder}.
 637  
      *
 638  
      * @param elementName   The name of the node where to add the attribute.
 639  
      * @param attributeName The name of the attribute to add in the given element.
 640  
      * @param mandatory     {@code true} if the attribute is mandatory, or {@code false} for optional.
 641  
      * @param codes         The enumeration of valid attribute values.
 642  
      *
 643  
      * @see #addAttribute(String, String, int, boolean, String, List)
 644  
      */
 645  
     final void addEnumeration(final String elementName, final String attributeName,
 646  
             final boolean mandatory, final String... codes)
 647  
     {
 648  14
         addAttribute(elementName, attributeName, DATATYPE_STRING, mandatory, null, Arrays.asList(codes));
 649  14
     }
 650  
 
 651  
     /**
 652  
      * Adds a new attribute of the given type. This method delegates to one of the {@code addAttribute}
 653  
      * methods defined in the super-class. The choice of method and parameters is performed according
 654  
      * the following rules (non-exhaustive list):
 655  
      * <p>
 656  
      * <ul>
 657  
      *   <li>The attribute is declared mandatory if {@code minOccurrence} is different than zero.</li>
 658  
      *   <li>The value type is set to {@link #VALUE_RANGE} if the {@code range} parameter is non-null.</li>
 659  
      *   <li>The value type is set to {@link #VALUE_LIST} if the {@code maxOccurrence} parameter is different than 1.</li>
 660  
      *   <li>The value type is set to {@link #VALUE_ARBITRARY} otherwise (except for boolean values).</li>
 661  
      * <p>
 662  
      * This method is defined mostly in order to give access to protected methods
 663  
      * from {@link SpatialMetadataFormatBuilder}.
 664  
      *
 665  
      * @param elementName   The name of the node where to add the attribute.
 666  
      * @param attributeName The name of the attribute to add in the given element.
 667  
      * @param dataType      The type of the attribute to add.
 668  
      * @param minOccurrence the smallest legal number of list items (typically 0 or 1).
 669  
      * @param maxOccurrence the largest legal number of list items (typically 1 or more).
 670  
      * @param range         The range of valid values, or {@code null} if none.
 671  
      *
 672  
      * @see #addBooleanAttribute(String, String, boolean, boolean)
 673  
      * @see #addAttribute(String, String, int, boolean, int, int)
 674  
      * @see #addAttribute(String, String, int, boolean, String, List)
 675  
      * @see #addAttribute(String, String, int, boolean, String, String, String, boolean, boolean)
 676  
      */
 677  
     final void addAttribute(final String elementName, final String attributeName,
 678  
             int dataType, final int minOccurrence, final int maxOccurrence, final NumberRange<?> range)
 679  
     {
 680  90
         if (dataType == IIOMetadataFormat.DATATYPE_BOOLEAN) {
 681  
             /*
 682  
              * Boolean  ⇒  Attribute VALUE_ENUMERATION
 683  
              *
 684  
              * A default value (false) is provided only if the attribute is
 685  
              * not mandatory (minOccurrence == 0), otherwise we will require
 686  
              * the user to specify a value explicitly.
 687  
              */
 688  6
             addBooleanAttribute(elementName, attributeName, minOccurrence == 0, false);
 689  84
         } else if (range != null) {
 690  
             /*
 691  
              * Number  ⇒  Attribute VALUE_RANGE[_?_INCLUSIVE]
 692  
              */
 693  14
             addAttribute(elementName, attributeName, dataType, minOccurrence != 0, null,
 694  
                     toString(range.getMinValue()), toString(range.getMaxValue()),
 695  
                     range.isMinIncluded(), range.isMaxIncluded());
 696  70
         } else if (maxOccurrence == 1) {
 697  
             /*
 698  
              * Object  ⇒  Attribute VALUE_ARBITRARY
 699  
              */
 700  59
             addAttribute(elementName, attributeName, dataType, minOccurrence != 0, null);
 701  
         } else {
 702  
             /*
 703  
              * Object  ⇒  Attribute VALUE_LIST
 704  
              */
 705  11
             addAttribute(elementName, attributeName, dataType, minOccurrence != 0, minOccurrence, maxOccurrence);
 706  
         }
 707  90
     }
 708  
 
 709  
     /**
 710  
      * Invoked when a metadata element is about to be added to the tree. This is a hook for adding
 711  
      * custom attributes which may not be in the UML or excluded. The main attributes of interest
 712  
      * are {@code "name"} and {@code "type"} for referencing objects.
 713  
      */
 714  
     final void addCustomAttributes(final String elementName, final Class<?> type) {
 715  31
         if (IdentifiedObject.class.isAssignableFrom(type)) {
 716  7
             addAttribute(elementName, "name", DATATYPE_STRING, true, null);
 717  
         }
 718  31
         if (CoordinateSystemAxis.class.isAssignableFrom(type)) {
 719  1
             addAttribute(elementName, "axisAbbrev", DATATYPE_STRING, true, null);
 720  
         }
 721  
         final List<String> types;
 722  31
         if (CoordinateReferenceSystem.class.isAssignableFrom(type)) {
 723  1
             types = DataTypes.CRS_TYPES;
 724  30
         } else if (CoordinateSystem.class.isAssignableFrom(type)) {
 725  1
             types = DataTypes.CS_TYPES;
 726  29
         } else if (Datum.class.isAssignableFrom(type)) {
 727  1
             types = DataTypes.DATUM_TYPES;
 728  
         } else {
 729  28
             return;
 730  
         }
 731  3
         addAttribute(elementName, "type", DATATYPE_STRING, true, null, types);
 732  3
     }
 733  
 
 734  
     /**
 735  
      * Remembers the name of child element or attribute for the given method.
 736  
      * This information is required by {@link MetadataProxy}.
 737  
      *
 738  
      * @param  parentName The name of the parent element.
 739  
      * @param  The name of the method which is mapped to an element/attribute in the parent element.
 740  
      * @param  elementName The name of the element/attribute which represents the method value.
 741  
      * @throws IllegalArgumentException If the parent element doesn't exist, or if the given method
 742  
      *         is already defined for the given parent.
 743  
      */
 744  
     final void mapName(final String parentName, final String methodName, final String elementName)
 745  
             throws IllegalArgumentException
 746  
     {
 747  
         // Actually 'methodName' is the only parameter which has not been verified
 748  
         // by the caller, but we nevertheless verify all parameters as a safety.
 749  119
         ensureNonNull("parentName",  parentName);
 750  119
         ensureNonNull("methodName",  methodName);
 751  119
         ensureNonNull("elementName", elementName);
 752  119
         Map<String, String> map = namesMapping.get(parentName);
 753  119
         if (map == null) {
 754  29
             map = new HashMap<String, String>();
 755  29
             namesMapping.put(parentName, map);
 756  
         }
 757  119
         final String old = map.put(methodName, elementName);
 758  119
         if (old != null && !old.equals(elementName)) {
 759  0
             map.put(methodName, old); // Preserve the previous value.
 760  0
             throw new IllegalArgumentException(Errors.format(
 761  
                     Errors.Keys.DUPLICATED_VALUES_FOR_KEY_$1, methodName));
 762  
         }
 763  119
     }
 764  
 
 765  
     /**
 766  
      * Makes the first character an upper-case letter. This is used for element names,
 767  
      * which typically starts with an upper-case letter in Image I/O metadata.
 768  
      *
 769  
      * @param  elementName The element name, or {@code null}.
 770  
      * @return The given name with the first character converted to an upper-case letter,
 771  
      *         or {@code null} if the given argument was null.
 772  
      *
 773  
      * @since 3.06
 774  
      */
 775  
     static String toElementName(String elementName) {
 776  87
         if (elementName != null && !(elementName = elementName.trim()).isEmpty()) {
 777  87
             final char c = elementName.charAt(0);
 778  87
             final char u = Character.toUpperCase(c);
 779  87
             if (c != u) {
 780  34
                 final StringBuilder buffer = new StringBuilder(elementName);
 781  34
                 buffer.setCharAt(0, u);
 782  34
                 elementName = buffer.toString();
 783  
             }
 784  
         }
 785  87
         return elementName;
 786  
     }
 787  
 
 788  
     /**
 789  
      * Returns a string representation of the given value, or
 790  
      * {@code null} if that value is null (unbounded range).
 791  
      */
 792  
     private static String toString(final Comparable<?> value) {
 793  28
         return (value != null) ? value.toString() : null;
 794  
     }
 795  
 
 796  
     /**
 797  
      * Removes an attribute from a previously defined element. If no attribute with the given
 798  
      * name was present in the given element, nothing happens and no exception is thrown.
 799  
      *
 800  
      * @param elementName The name of the element.
 801  
      * @param attributeName The name of the attribute being removed.
 802  
      */
 803  
     @Override
 804  
     protected void removeAttribute(final String elementName, final String attributeName) {
 805  
         // This method is overriden only in order to allow SpatialMetadataFormatBuilder to access it.
 806  4
         super.removeAttribute(elementName, attributeName);
 807  4
     }
 808  
 
 809  
     /**
 810  
      * Removes an element from the format. If no element with the given
 811  
      * name was present, nothing happens and no exception is thrown.
 812  
      *
 813  
      * @param elementName the name of the element to be removed.
 814  
      */
 815  
     @Override
 816  
     protected void removeElement(final String elementName) {
 817  0
         super.removeElement(elementName);
 818  0
         standards   .remove(elementName);
 819  0
         namesMapping.remove(elementName);
 820  0
     }
 821  
 
 822  
     /**
 823  
      * Returns {@code true} if the element (and the subtree below it) is allowed to appear
 824  
      * in a metadata document for an image of the given type. The default implementation
 825  
      * always returns {@code true}.
 826  
      */
 827  
     @Override
 828  
     public boolean canNodeAppear(final String elementName, final ImageTypeSpecifier imageType) {
 829  0
         return true;
 830  
     }
 831  
 
 832  
     /**
 833  
      * Returns the element which is the parent of the named element, or {@code null} if none.
 834  
      * For example if this metadata format is the {@linkplain #getStreamInstance(String) stream}
 835  
      * instance, then:
 836  
      * <p>
 837  
      * <ul>
 838  
      *   <li>The path to {@code "GeographicElement"} is {@code "DiscoveryMetadata/Extent/GeographicElement"}.</li>
 839  
      *   <li>The parent of {@code "GeographicElement"} returned by this method is {@code "Extent"}.</li>
 840  
      * </ul>
 841  
      *
 842  
      * {@note An element may have more than one parent, since the same element can be copied under
 843  
      *        many nodes using <code>addChildElement(...)</code>. In such case, this method returns
 844  
      *        only the first path. Such cases do not occur with the Geotk formats identified by
 845  
      *        <code>FORMAT_NAME</code> in this class, but occur with the more complex ISO-19115
 846  
      *        format.}
 847  
      *
 848  
      * @param  elementName The element for which the parent is desired.
 849  
      * @return The parent of the given element, or {@code null}.
 850  
      *
 851  
      * @see #getElementPath(String)
 852  
      *
 853  
      * @since 3.06
 854  
      */
 855  
     public String getElementParent(final String elementName) {
 856  68
         ensureNonNull("elementName", elementName);
 857  68
         return getElementParent(getRootName(), elementName, null);
 858  
     }
 859  
 
 860  
     /**
 861  
      * Returns the element which is the parent of the named element, or {@code null} if none.
 862  
      * <p>
 863  
      * <b>Note:</b> Current implementation is somewhat inefficient.  We could maintain a map of
 864  
      * parents when new elements are added, but {@link IIOMetadataFormatImpl} already maintains
 865  
      * such map - I'm not sure why they do no provide API for getting that info. This API could
 866  
      * have been implemented as:
 867  
      *
 868  
      * {@preformat java
 869  
      *     public String[] getElementParents(String elementName) {
 870  
      *         List<String> parents = getElement(elementName).parentList;
 871  
      *         return parents.toString(new String[parents.size()]);
 872  
      *     }
 873  
      * }
 874  
      *
 875  
      * @param  root The root element from which to starts the scan.
 876  
      * @param  elementName The element for which the parent is desired.
 877  
      * @param  path If non-null, a buffer where to append the path before the node.
 878  
      * @return The parent of the given element, or {@code null}.
 879  
      */
 880  
     private String getElementParent(final String root, final String elementName, final StringBuilder path) {
 881  762
         final String[] childs = getChildNames(root);
 882  762
         if (childs != null) {
 883  1395
             for (final String child : childs) {
 884  919
                 if (child.equals(elementName)) {
 885  85
                     return root;
 886  
                 }
 887  
             }
 888  
             // Do recursive call only after we checked every childs at the root. If a name
 889  
             // appears twice (it should not), we will favor the one at the lowest depth.
 890  1024
             for (final String child : childs) {
 891  667
                 final String candidate = getElementParent(child, elementName, path);
 892  667
                 if (candidate != null) {
 893  119
                     if (path != null) {
 894  28
                         path.insert(0, '/').insert(0, child);
 895  
                     }
 896  119
                     return candidate;
 897  
                 }
 898  
             }
 899  
         }
 900  558
         return null;
 901  
     }
 902  
 
 903  
     /**
 904  
      * Returns the path to the named element, or {@code null} if none. For example if this
 905  
      * metadata format is the {@linkplain #getStreamInstance stream} instance, then the path to the
 906  
      * {@code "GeographicElement"} is {@code "DiscoveryMetadata/Extent/GeographicElement"}.
 907  
      *
 908  
      * {@note An element may have more than one path, since the same element can be copied under
 909  
      *        many nodes using <code>addChildElement(...)</code>. In such case, this method returns
 910  
      *        only the first path. Such cases do not occur with the Geotk formats identified by
 911  
      *        <code>FORMAT_NAME</code> in this class, but occur with the more complex ISO-19115
 912  
      *        format.}
 913  
      *
 914  
      * @param  elementName The element for which the path is desired.
 915  
      * @return The path to the given element, or {@code null}.
 916  
      *
 917  
      * @see #getElementParent(String)
 918  
      *
 919  
      * @since 3.06
 920  
      */
 921  
     public String getElementPath(final String elementName) {
 922  27
         ensureNonNull("elementName", elementName);
 923  27
         final StringBuilder path = new StringBuilder();
 924  27
         final String parent = getElementParent(getRootName(), elementName, path);
 925  27
         if (parent != null) {
 926  
             // The parent is already in the path at this point.
 927  23
             return path.append(elementName).toString();
 928  
         }
 929  4
         return null;
 930  
     }
 931  
 
 932  
     /**
 933  
      * Returns the metadata standard implemented by the element of the given name.
 934  
      * If the given element does not implement a standard (which may happen if the
 935  
      * element has not been added by {@link SpatialMetadataFormatBuilder} method),
 936  
      * then this method returns {@code null}.
 937  
      *
 938  
      * @param  elementName The element for which the standard is desired.
 939  
      * @return The standard implemented by the given element, or {@code null}.
 940  
      *
 941  
      * @since 3.06
 942  
      */
 943  
     public MetadataStandard getElementStandard(final String elementName) {
 944  45
         return standards.get(elementName);
 945  
     }
 946  
 
 947  
     /**
 948  
      * Returns the mapping from method names to element/attribute names, or {@code null} if this
 949  
      * mapping is unknown. Keys are method names, and values are the attribute name as determined
 950  
      * by {@link SpatialMetadataFormat#NAME_POLICY}.
 951  
      * <p>
 952  
      * This method returns a direct reference to the internal map.
 953  
      * <strong>Do not modify the map content!</strong>
 954  
      */
 955  
     final Map<String, String> getElementNames(final String elementName) {
 956  433
         return namesMapping.get(elementName);
 957  
     }
 958  
 
 959  
     /**
 960  
      * Returns a description of the named element, or {@code null}. The description will be
 961  
      * localized for the supplied locale if possible.
 962  
      * <p>
 963  
      * The default implementation first queries the
 964  
      * {@linkplain MetadataStandard#asDescriptionMap description map} associated with the
 965  
      * {@linkplain #getElementStandard metadata standard}. If no description is found, then the
 966  
      * {@linkplain IIOMetadataFormatImpl#getElementDescription super-class implementation}
 967  
      * is used.
 968  
      *
 969  
      * @param  elementName The name of the element.
 970  
      * @param  locale The Locale for which localization will be attempted, or null.
 971  
      * @return The attribute description.
 972  
      *
 973  
      * @since 3.05
 974  
      */
 975  
     @Override
 976  
     public String getElementDescription(final String elementName, final Locale locale) {
 977  41
         ensureNonNull("elementName", elementName);
 978  41
         String description = getDescription(elementName, null, locale);
 979  41
         if (description == null) {
 980  25
             description = super.getElementDescription(elementName, locale);
 981  
         }
 982  41
         return description;
 983  
     }
 984  
 
 985  
     /**
 986  
      * Returns a description of the named attribute, or {@code null}. The description will be
 987  
      * localized for the supplied locale if possible.
 988  
      * <p>
 989  
      * The default implementation first queries the
 990  
      * {@linkplain MetadataStandard#asDescriptionMap description map} associated with the
 991  
      * {@linkplain #getElementStandard metadata standard}. If no description is found, then the
 992  
      * {@linkplain IIOMetadataFormatImpl#getAttributeDescription super-class implementation}
 993  
      * is used.
 994  
      *
 995  
      * @param  elementName The name of the element.
 996  
      * @param  attrName    The name of the attribute.
 997  
      * @param  locale      The Locale for which localization will be attempted, or null.
 998  
      * @return The attribute description.
 999  
      *
 1000  
      * @since 3.05
 1001  
      */
 1002  
     @Override
 1003  
     public String getAttributeDescription(final String elementName, final String attrName, final Locale locale) {
 1004  112
         ensureNonNull("elementName", elementName);
 1005  112
         ensureNonNull("attrName",    attrName);
 1006  112
         String description = getDescription(elementName, attrName, locale);
 1007  112
         if (description == null) {
 1008  48
             description = super.getAttributeDescription(elementName, attrName, locale);
 1009  
         }
 1010  112
         return description;
 1011  
     }
 1012  
 
 1013  
     /**
 1014  
      * Returns the description of the given attribute of the given element, in the given locale.
 1015  
      * If the attribute is null, then this method assumes that the caller want the description
 1016  
      * of the element itself. If there is no description available, returns {@code null}.
 1017  
      *
 1018  
      * @param  elementName The name of the element in which to search for attributes.
 1019  
      * @param  attrName The name of the attribute for which the descriptions is desired, or {@code null}.
 1020  
      * @param  locale The locale of the descriptions, or {@code null} for the default.
 1021  
      * @return The requested description, or {@code null} if none.
 1022  
      */
 1023  
     private String getDescription(String elementName, String attrName, Locale locale) {
 1024  153
         if (locale == null) {
 1025  0
             locale = Locale.getDefault();
 1026  
         }
 1027  153
         if (attrName == null) {
 1028  41
             attrName = elementName;
 1029  41
             elementName = getElementParent(elementName);
 1030  41
             if (elementName == null) {
 1031  2
                 return null;
 1032  
             }
 1033  
         }
 1034  151
         MetadataDescriptions candidate = descriptions;
 1035  151
         if (candidate == null || !locale.equals(candidate.locale) || !elementName.equals(candidate.elementName)) {
 1036  57
             Class<?> type = null;
 1037  
             try {
 1038  57
                 type = getObjectClass(elementName);
 1039  12
             } catch (IllegalArgumentException e) {
 1040  
                 // The given element does not allow the storage of objects.
 1041  
                 // We will set the description map to an empty map.
 1042  45
             }
 1043  57
             Map<String,String> desc = Collections.emptyMap();
 1044  57
             if (type != null) {
 1045  45
                 final MetadataStandard standard = getElementStandard(elementName);
 1046  45
                 if (standard != null) try {
 1047  43
                     desc = standard.asDescriptionMap(type, locale, NAME_POLICY);
 1048  2
                 } catch (ClassCastException e) {
 1049  
                     // The element type is not an instance of the expected standard.
 1050  
                     // We will set the description map to an empty map.
 1051  41
                 }
 1052  
             }
 1053  57
             candidate = new MetadataDescriptions(desc, elementName, locale);
 1054  57
             descriptions = candidate;
 1055  
         }
 1056  151
         return candidate.descriptions.get(attrName);
 1057  
     }
 1058  
 
 1059  
     /**
 1060  
      * Returns the default value for an object reference of the given type. This method is
 1061  
      * invoked automatically by {@link SpatialMetadataFormatBuilder} for determining the value
 1062  
      * of the {@code defaultValue} argument in the call to the {@link #addObjectValue(String,
 1063  
      * Class, boolean, Object) addObjectValue} method.
 1064  
      * <p>
 1065  
      * This method is also invoked by {@link ReferencingBuilder#getDefault(Class)}, which does not
 1066  
      * rely on {@link IIOMetadataFormat#getObjectDefaultValue(String)} because the default value of
 1067  
      * some referencing objects depends on the type of the enclosing element. For example the default
 1068  
      * coordinate system shall be ellipsoidal for a geographic CRS and Cartesian for a projected
 1069  
      * CRS.
 1070  
      * <p>
 1071  
      * The default implementation returns a value determined from the table below.
 1072  
      * Subclasses can override this method for providing different default values.
 1073  
      * <p>
 1074  
      * <table border="1" cellspacing="0">
 1075  
      * <tr bgcolor="lightblue">
 1076  
      *   <th>Type</th>
 1077  
      *   <th>Default value</th>
 1078  
      * </tr><tr>
 1079  
      *   <td>&nbsp;{@link PrimeMeridian}&nbsp;</td>
 1080  
      *   <td>&nbsp;{@link DefaultPrimeMeridian#GREENWICH}&nbsp;</td>
 1081  
      * </tr><tr>
 1082  
      *   <td>&nbsp;{@link Ellipsoid}&nbsp;</td>
 1083  
      *   <td>&nbsp;{@link DefaultEllipsoid#WGS84}&nbsp;</td>
 1084  
      * </tr><tr>
 1085  
      *   <td>&nbsp;{@link GeodeticDatum}&nbsp;</td>
 1086  
      *   <td>&nbsp;{@link DefaultGeodeticDatum#WGS84}&nbsp;</td>
 1087  
      * </tr><tr>
 1088  
      *   <td>&nbsp;{@link VerticalDatum}&nbsp;</td>
 1089  
      *   <td>&nbsp;{@link DefaultVerticalDatum#GEOIDAL}&nbsp;</td>
 1090  
      * </tr><tr>
 1091  
      *   <td>&nbsp;{@link EngineeringDatum}&nbsp;</td>
 1092  
      *   <td>&nbsp;{@link DefaultEngineeringDatum#UNKNOWN}&nbsp;</td>
 1093  
      * </tr><tr>
 1094  
      *   <td>&nbsp;{@link EllipsoidalCS}&nbsp;</td>
 1095  
      *   <td>&nbsp;{@link DefaultEllipsoidalCS#GEODETIC_2D}&nbsp;</td>
 1096  
      * </tr><tr>
 1097  
      *   <td>&nbsp;{@link CartesianCS}&nbsp;</td>
 1098  
      *   <td>&nbsp;{@link DefaultCartesianCS#GENERIC_2D}&nbsp;</td>
 1099  
      * </tr><tr>
 1100  
      *   <td>&nbsp;{@link GeographicCRS}&nbsp;</td>
 1101  
      *   <td>&nbsp;{@link DefaultGeographicCRS#WGS84}&nbsp;</td>
 1102  
      * </tr><tr>
 1103  
      *   <td>&nbsp;{@link GeocentricCRS}&nbsp;</td>
 1104  
      *   <td>&nbsp;{@link DefaultGeocentricCRS#CARTESIAN}&nbsp;</td>
 1105  
      * </tr><tr>
 1106  
      *   <td>&nbsp;All other type&nbsp;</td>
 1107  
      *   <td>&nbsp;{@code null}&nbsp;</td>
 1108  
      * </tr>
 1109  
      * </table>
 1110  
      *
 1111  
      * @param  <T> The compile-time type of {@code classType}.
 1112  
      * @param  type The class type of the object for which to get a default value.
 1113  
      * @return The default value for an object of the given type, or {@code null} if none.
 1114  
      *
 1115  
      * @see ReferencingBuilder#getDefault(Class)
 1116  
      * @see #getObjectDefaultValue(String)
 1117  
      *
 1118  
      * @since 3.08
 1119  
      */
 1120  
     public <T> T getDefaultValue(final Class<T> type) {
 1121  
         final IdentifiedObject object;
 1122  34
         if (PrimeMeridian.class.isAssignableFrom(type)) {
 1123  1
             object = DefaultPrimeMeridian.GREENWICH;
 1124  33
         } else if (Ellipsoid.class.isAssignableFrom(type)) {
 1125  1
             object = DefaultEllipsoid.WGS84;
 1126  32
         } else if (GeodeticDatum.class.isAssignableFrom(type)) {
 1127  1
             object = DefaultGeodeticDatum.WGS84;
 1128  31
         } else if (VerticalDatum.class.isAssignableFrom(type)) {
 1129  0
             object = DefaultVerticalDatum.GEOIDAL;
 1130  31
         } else if (EngineeringDatum.class.isAssignableFrom(type)) {
 1131  0
             object = DefaultEngineeringDatum.UNKNOWN;
 1132  31
         } else if (EllipsoidalCS.class.isAssignableFrom(type)) {
 1133  0
             object = DefaultEllipsoidalCS.GEODETIC_2D;
 1134  31
         } else if (CartesianCS.class.isAssignableFrom(type)) {
 1135  0
             object = DefaultCartesianCS.GENERIC_2D;
 1136  31
         } else if (GeographicCRS.class.isAssignableFrom(type)) {
 1137  0
             object = DefaultGeographicCRS.WGS84;
 1138  31
         } else if (GeocentricCRS.class.isAssignableFrom(type)) {
 1139  0
             object = DefaultGeocentricCRS.CARTESIAN;
 1140  
         } else {
 1141  31
             object = null;
 1142  
         }
 1143  34
         return type.cast(object);
 1144  
     }
 1145  
 
 1146  
     /**
 1147  
      * Returns a <cite>tree table</cite> representation of this metadata standard.
 1148  
      * This convenience method delegates the work to {@link MetadataTreeTable}.
 1149  
      *
 1150  
      * @param  locale The locale for which localization will be attempted, or {@code null}.
 1151  
      * @return A tree representation of this metadata standard.
 1152  
      */
 1153  
     public TreeTableNode toTreeTable(final Locale locale) {
 1154  2
         final MetadataTreeTable tree = new MetadataTreeTable(this);
 1155  2
         if (locale != null) {
 1156  0
             tree.setLocale(locale);
 1157  
         }
 1158  2
         return tree.getRootNode();
 1159  
     }
 1160  
 
 1161  
     /**
 1162  
      * Returns a string representation of this format.
 1163  
      * The default implementation formats this object as a tree.
 1164  
      */
 1165  
     @Override
 1166  
     public String toString() {
 1167  2
         return Trees.toString(toTreeTable(null));
 1168  
     }
 1169  
 }