You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2018/07/03 13:39:54 UTC
[sis] 01/01: Merge branch 'geoapi-3.1'.
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sis.git
commit 969b7d05881b30523939aa8b9d825183ae23195f
Merge: 2063b69 585bddd
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Tue Jul 3 15:37:52 2018 +0200
Merge branch 'geoapi-3.1'.
.gitattributes | 44 ++
.../java/org/apache/sis/console/package-info.java | 2 +-
.../src/main/java/org/apache/sis/gui/Main.java | 204 ++++++
.../org/apache/sis/gui/dataset/FeatureTable.java | 169 +++++
.../org/apache/sis/gui/dataset/ResourceTree.java | 23 +-
.../org/apache/sis/gui/metadata/MetadataNode.java | 228 ++++++
.../apache/sis/gui/metadata/MetadataOverview.java | 403 ++++++++++
.../org/apache/sis/gui/metadata/ResourceView.java | 399 ++++++++++
.../sis/gui/{crs => referencing}/CRSButton.java | 2 +-
.../sis/gui/{crs => referencing}/CRSChooser.java | 81 +-
.../sis/gui/{crs => referencing}/CRSTable.java | 69 +-
.../apache/sis/gui/{crs => referencing}/Code.java | 54 +-
.../WKTPane.java} | 16 +-
.../org/apache/sis/internal/gui/FXUtilities.java | 91 +++
.../org/apache/sis/internal/gui/FontGlyphs.java | 13 +-
.../apache/sis/internal/gui/JavaFxUtilities.java | 98 ---
.../apache/sis/{ => internal}/gui/Resources.java | 94 +--
.../sis/{ => internal}/gui/Resources.properties | 9 +-
.../sis/{ => internal}/gui/Resources_fr.properties | 9 +-
.../org/apache/sis/internal/gui}/package-info.java | 10 +-
.../org/apache/sis/gui/crs/CRSChooser.fxml | 36 -
.../apache/sis/gui/metadata/WorldMap360x180.png | Bin 0 -> 1886 bytes
.../org/apache/sis/gui/referencing/CRSChooser.fxml | 30 +
.../sis/gui/{crs => referencing}/proj_conic.png | Bin
.../sis/gui/{crs => referencing}/proj_geo.png | Bin
.../sis/gui/{crs => referencing}/proj_square.png | Bin
.../sis/gui/{crs => referencing}/proj_stereo.png | Bin
.../sis/gui/{crs => referencing}/proj_utm.png | Bin
.../org/apache/sis/test/suite/package-info.txt | 3 +
.../apache/sis/feature/AbstractAssociation.java | 2 -
.../org/apache/sis/feature/AbstractAttribute.java | 2 -
.../org/apache/sis/feature/AbstractOperation.java | 2 -
.../apache/sis/feature/DefaultAssociationRole.java | 2 -
.../apache/sis/feature/DefaultAttributeType.java | 2 -
.../java/org/apache/sis/feature/PropertyView.java | 2 -
.../apache/sis/feature/builder/TypeBuilder.java | 2 -
.../org/apache/sis/feature/CustomAttribute.java | 2 +-
.../java/org/apache/sis/internal/jaxb/Context.java | 1 +
.../sis/internal/jaxb/IdentifierMapAdapter.java | 3 -
.../sis/internal/jaxb/NonMarshalledAuthority.java | 18 +-
.../sis/internal/jaxb/SpecializedIdentifier.java | 2 -
.../apache/sis/internal/jaxb/cat/CodeListUID.java | 2 +-
.../internal/jaxb/code/MD_CharacterSetLegacy.java | 2 +-
.../sis/internal/jaxb/code/MD_MediumNameCode.java | 2 +-
.../sis/internal/jaxb/code/package-info.java | 2 +-
.../sis/internal/jaxb/gco/GO_CharacterString.java | 2 +-
.../apache/sis/internal/jaxb/gco/GO_DateTime.java | 2 +-
.../apache/sis/internal/jaxb/gco/GO_Decimal.java | 2 +-
.../apache/sis/internal/jaxb/gco/GO_Integer.java | 7 +-
.../apache/sis/internal/jaxb/gco/GO_Integer64.java | 4 +-
...GO_Integer64.java => GO_MultiplicityRange.java} | 53 +-
.../apache/sis/internal/jaxb/gco/GO_Record.java | 2 +-
.../sis/internal/jaxb/gco/GO_RecordType.java | 2 +-
...{GO_Integer64.java => GO_UnlimitedInteger.java} | 46 +-
.../apache/sis/internal/jaxb/gco/Multiplicity.java | 92 +++
.../sis/internal/jaxb/gco/MultiplicityRange.java | 107 +++
.../sis/internal/jaxb/gco/UnlimitedInteger.java | 120 +++
.../sis/internal/jaxb/gmi/LE_ProcessStep.java | 1 -
.../apache/sis/internal/jaxb/gmi/LE_Source.java | 1 -
.../org/apache/sis/internal/jaxb/gmi/MI_Band.java | 1 -
.../internal/jaxb/gmi/MI_CoverageDescription.java | 1 -
.../sis/internal/jaxb/gmi/MI_Georectified.java | 1 -
.../sis/internal/jaxb/gmi/MI_Georeferenceable.java | 1 -
.../sis/internal/jaxb/gmi/MI_ImageDescription.java | 1 -
.../apache/sis/internal/jaxb/gmi/MI_Metadata.java | 3 +-
.../apache/sis/internal/jaxb/gmi/package-info.java | 2 +-
.../apache/sis/internal/jaxb/gml/DateAdapter.java | 2 +-
.../org/apache/sis/internal/jaxb/gml/Measure.java | 2 +-
.../apache/sis/internal/jaxb/gml/TM_Primitive.java | 2 +-
.../apache/sis/internal/jaxb/gml/TimeInstant.java | 2 +-
.../apache/sis/internal/jaxb/gml/TimePeriod.java | 2 +-
.../internal/jaxb/gml/UniversalTimeAdapter.java | 6 +-
.../sis/internal/jaxb/gts/TM_PeriodDuration.java | 2 +-
.../org/apache/sis/internal/jaxb/lan/Country.java | 2 +-
.../jaxb/lan/LocalisedCharacterString.java | 2 -
.../apache/sis/internal/jaxb/lan/PT_Locale.java | 2 +-
.../apache/sis/internal/jaxb/lan/TextGroup.java | 2 -
.../jaxb/metadata/CI_ResponsibleParty.java | 4 +-
.../sis/internal/jaxb/metadata/RS_Identifier.java | 11 +-
.../metadata/replace/ReferenceSystemMetadata.java | 2 +-
.../jaxb/metadata/replace/ServiceParameter.java | 2 +-
.../jaxb/metadata/replace/package-info.java | 2 +-
.../org/apache/sis/internal/jaxb/package-info.java | 1 +
.../sis/internal/metadata/MetadataTypes.java | 4 +-
.../sis/internal/metadata/ReferencingServices.java | 16 +-
.../sis/internal/metadata/sql/SQLBuilder.java | 16 +-
.../sis/internal/simple/CitationConstant.java | 2 -
.../apache/sis/internal/simple/SimpleCitation.java | 2 -
.../apache/sis/internal/simple/SimpleFormat.java | 2 -
.../sis/internal/simple/SimpleIdentifier.java | 2 -
.../internal/{jaxb => xml}/LegacyNamespaces.java | 2 +-
.../apache/sis/internal/{jaxb => xml}/Schemas.java | 2 +-
.../sis/internal/xml}/StreamWriterDelegate.java | 4 +-
.../sis/internal/{jaxb => xml}/XmlUtilities.java | 3 +-
.../org/apache/sis/internal/xml}/package-info.java | 16 +-
.../main/java/org/apache/sis/io/wkt/Element.java | 1 -
.../main/java/org/apache/sis/io/wkt/Formatter.java | 2 -
.../org/apache/sis/metadata/AbstractMetadata.java | 29 +-
.../java/org/apache/sis/metadata/CacheKey.java | 2 -
.../java/org/apache/sis/metadata/HashCode.java | 105 +++
.../org/apache/sis/metadata/MetadataCopier.java | 180 +++--
.../org/apache/sis/metadata/MetadataStandard.java | 57 +-
.../org/apache/sis/metadata/MetadataVisitor.java | 286 ++++++++
.../sis/metadata/MetadataVisitorException.java | 100 +++
.../apache/sis/metadata/ModifiableMetadata.java | 309 +++++---
.../java/org/apache/sis/metadata/ObjectPair.java | 2 -
.../org/apache/sis/metadata/PropertyAccessor.java | 205 +++---
.../main/java/org/apache/sis/metadata/Pruner.java | 281 +++----
.../org/apache/sis/metadata/RecursivityGuard.java | 69 --
.../sis/metadata/StandardImplementation.java | 2 +-
.../metadata/{Freezer.java => StateChanger.java} | 121 ++-
.../java/org/apache/sis/metadata/TreeNode.java | 2 -
.../org/apache/sis/metadata/TreeNodeChildren.java | 2 -
.../metadata/UnmodifiableMetadataException.java | 5 +-
.../iso/DefaultApplicationSchemaInformation.java | 1 -
.../iso/DefaultExtendedElementInformation.java | 3 +-
.../apache/sis/metadata/iso/DefaultIdentifier.java | 1 -
.../apache/sis/metadata/iso/DefaultMetadata.java | 18 +-
.../iso/DefaultMetadataExtensionInformation.java | 1 -
.../sis/metadata/iso/DefaultMetadataScope.java | 1 -
.../iso/DefaultPortrayalCatalogueReference.java | 1 -
.../org/apache/sis/metadata/iso/ISOMetadata.java | 72 +-
.../sis/metadata/iso/ImmutableIdentifier.java | 2 +
.../sis/metadata/iso/MetadataScopeAdapter.java | 2 +-
.../acquisition/DefaultAcquisitionInformation.java | 1 -
.../acquisition/DefaultEnvironmentalRecord.java | 1 -
.../sis/metadata/iso/acquisition/DefaultEvent.java | 9 +-
.../iso/acquisition/DefaultInstrument.java | 9 +-
.../metadata/iso/acquisition/DefaultObjective.java | 1 -
.../metadata/iso/acquisition/DefaultOperation.java | 9 +-
.../sis/metadata/iso/acquisition/DefaultPlan.java | 1 -
.../metadata/iso/acquisition/DefaultPlatform.java | 9 +-
.../iso/acquisition/DefaultPlatformPass.java | 9 +-
.../iso/acquisition/DefaultRequestedDate.java | 1 -
.../iso/acquisition/DefaultRequirement.java | 9 +-
.../sis/metadata/iso/citation/AbstractParty.java | 1 -
.../sis/metadata/iso/citation/Citations.java | 2 +-
.../sis/metadata/iso/citation/DefaultAddress.java | 1 -
.../sis/metadata/iso/citation/DefaultCitation.java | 3 +-
.../metadata/iso/citation/DefaultCitationDate.java | 1 -
.../sis/metadata/iso/citation/DefaultContact.java | 3 +-
.../metadata/iso/citation/DefaultIndividual.java | 1 -
.../iso/citation/DefaultOnlineResource.java | 1 -
.../metadata/iso/citation/DefaultOrganisation.java | 1 -
.../iso/citation/DefaultResponsibility.java | 2 +-
.../iso/citation/DefaultResponsibleParty.java | 3 +-
.../sis/metadata/iso/citation/DefaultSeries.java | 1 -
.../metadata/iso/citation/DefaultTelephone.java | 5 +-
.../sis/metadata/iso/citation/package-info.java | 2 +-
.../iso/constraint/DefaultConstraints.java | 1 -
.../iso/constraint/DefaultLegalConstraints.java | 1 -
.../iso/constraint/DefaultReleasability.java | 1 -
.../iso/constraint/DefaultSecurityConstraints.java | 1 -
.../iso/content/AbstractContentInformation.java | 1 -
.../iso/content/DefaultAttributeGroup.java | 1 -
.../sis/metadata/iso/content/DefaultBand.java | 1 -
.../iso/content/DefaultCoverageDescription.java | 3 +-
.../DefaultFeatureCatalogueDescription.java | 3 +-
.../iso/content/DefaultFeatureTypeInfo.java | 1 -
.../iso/content/DefaultImageDescription.java | 3 +-
.../iso/content/DefaultRangeDimension.java | 3 +-
.../content/DefaultRangeElementDescription.java | 1 -
.../iso/content/DefaultSampleDimension.java | 1 -
.../sis/metadata/iso/content/package-info.java | 2 +-
.../metadata/iso/distribution/DefaultDataFile.java | 3 +-
.../DefaultDigitalTransferOptions.java | 1 -
.../iso/distribution/DefaultDistribution.java | 1 -
.../iso/distribution/DefaultDistributor.java | 1 -
.../metadata/iso/distribution/DefaultFormat.java | 3 +-
.../metadata/iso/distribution/DefaultMedium.java | 11 +-
.../distribution/DefaultStandardOrderProcess.java | 1 -
.../metadata/iso/distribution/package-info.java | 2 +-
.../iso/extent/AbstractGeographicExtent.java | 1 -
.../iso/extent/DefaultBoundingPolygon.java | 1 -
.../sis/metadata/iso/extent/DefaultExtent.java | 1 -
.../iso/extent/DefaultGeographicBoundingBox.java | 1 -
.../iso/extent/DefaultGeographicDescription.java | 1 -
.../iso/extent/DefaultSpatialTemporalExtent.java | 1 -
.../metadata/iso/extent/DefaultTemporalExtent.java | 1 -
.../metadata/iso/extent/DefaultVerticalExtent.java | 1 -
.../iso/identification/AbstractIdentification.java | 3 +-
.../DefaultAggregateInformation.java | 3 +-
.../identification/DefaultAssociatedResource.java | 1 -
.../iso/identification/DefaultBrowseGraphic.java | 1 -
.../iso/identification/DefaultCoupledResource.java | 7 +-
.../identification/DefaultDataIdentification.java | 3 +-
.../iso/identification/DefaultKeywordClass.java | 1 -
.../iso/identification/DefaultKeywords.java | 1 -
.../DefaultOperationChainMetadata.java | 1 -
.../identification/DefaultOperationMetadata.java | 1 -
.../iso/identification/DefaultResolution.java | 1 -
.../DefaultServiceIdentification.java | 1 -
.../metadata/iso/identification/DefaultUsage.java | 1 -
.../metadata/iso/identification/package-info.java | 2 +-
.../sis/metadata/iso/lineage/DefaultAlgorithm.java | 1 -
.../sis/metadata/iso/lineage/DefaultLineage.java | 1 -
.../iso/lineage/DefaultNominalResolution.java | 1 -
.../metadata/iso/lineage/DefaultProcessStep.java | 3 +-
.../iso/lineage/DefaultProcessStepReport.java | 1 -
.../metadata/iso/lineage/DefaultProcessing.java | 9 +-
.../sis/metadata/iso/lineage/DefaultSource.java | 18 +-
.../sis/metadata/iso/lineage/package-info.java | 2 +-
.../maintenance/DefaultMaintenanceInformation.java | 3 +-
.../sis/metadata/iso/maintenance/DefaultScope.java | 1 -
.../iso/maintenance/DefaultScopeDescription.java | 1 -
.../sis/metadata/iso/maintenance/package-info.java | 2 +-
.../org/apache/sis/metadata/iso/package-info.java | 2 +-
.../metadata/iso/quality/AbstractCompleteness.java | 1 -
.../sis/metadata/iso/quality/AbstractElement.java | 3 +-
.../iso/quality/AbstractLogicalConsistency.java | 1 -
.../iso/quality/AbstractPositionalAccuracy.java | 1 -
.../sis/metadata/iso/quality/AbstractResult.java | 1 -
.../iso/quality/AbstractTemporalAccuracy.java | 1 -
.../iso/quality/AbstractThematicAccuracy.java | 1 -
.../DefaultAbsoluteExternalPositionalAccuracy.java | 1 -
.../quality/DefaultAccuracyOfATimeMeasurement.java | 1 -
.../iso/quality/DefaultCompletenessCommission.java | 1 -
.../iso/quality/DefaultCompletenessOmission.java | 1 -
.../iso/quality/DefaultConceptualConsistency.java | 1 -
.../iso/quality/DefaultConformanceResult.java | 1 -
.../iso/quality/DefaultCoverageResult.java | 1 -
.../metadata/iso/quality/DefaultDataQuality.java | 3 +-
.../iso/quality/DefaultDomainConsistency.java | 1 -
.../iso/quality/DefaultFormatConsistency.java | 1 -
.../DefaultGriddedDataPositionalAccuracy.java | 1 -
.../DefaultNonQuantitativeAttributeAccuracy.java | 1 -
.../DefaultQuantitativeAttributeAccuracy.java | 1 -
.../iso/quality/DefaultQuantitativeResult.java | 3 +-
.../DefaultRelativeInternalPositionalAccuracy.java | 1 -
.../sis/metadata/iso/quality/DefaultScope.java | 1 -
.../iso/quality/DefaultTemporalConsistency.java | 1 -
.../iso/quality/DefaultTemporalValidity.java | 1 -
.../DefaultThematicClassificationCorrectness.java | 1 -
.../iso/quality/DefaultTopologicalConsistency.java | 1 -
.../sis/metadata/iso/quality/DefaultUsability.java | 3 +-
.../sis/metadata/iso/quality/package-info.java | 2 +-
.../spatial/AbstractGeolocationInformation.java | 1 -
.../iso/spatial/AbstractSpatialRepresentation.java | 1 -
.../sis/metadata/iso/spatial/DefaultDimension.java | 1 -
.../sis/metadata/iso/spatial/DefaultGCP.java | 1 -
.../metadata/iso/spatial/DefaultGCPCollection.java | 1 -
.../iso/spatial/DefaultGeometricObjects.java | 1 -
.../metadata/iso/spatial/DefaultGeorectified.java | 1 -
.../iso/spatial/DefaultGeoreferenceable.java | 1 -
.../spatial/DefaultGridSpatialRepresentation.java | 1 -
.../DefaultVectorSpatialRepresentation.java | 1 -
.../java/org/apache/sis/metadata/package-info.java | 2 +-
.../apache/sis/metadata/sql/MetadataSource.java | 42 +-
.../apache/sis/metadata/sql/MetadataWriter.java | 59 +-
.../apache/sis/metadata/sql/TableHierarchy.java | 118 +++
.../org/apache/sis/util/iso/DefaultNameSpace.java | 2 -
.../org/apache/sis/util/iso/DefaultRecord.java | 2 -
.../apache/sis/util/iso/DefaultRecordSchema.java | 2 -
.../org/apache/sis/util/iso/RecordDefinition.java | 2 -
.../main/java/org/apache/sis/xml/InputFactory.java | 29 +-
.../main/java/org/apache/sis/xml/Namespaces.java | 5 +-
.../java/org/apache/sis/xml/OutputFactory.java | 31 +-
.../src/main/java/org/apache/sis/xml/Pooled.java | 2 +-
.../java/org/apache/sis/xml/TransformVersion.java | 2 +-
.../main/java/org/apache/sis/xml/Transformer.java | 22 +-
.../org/apache/sis/xml/TransformingReader.java | 29 +-
.../org/apache/sis/xml/TransformingWriter.java | 17 +-
.../internal/jaxb/cat/CodeListMarshallingTest.java | 4 +-
.../sis/internal/jaxb/gco/MultiplicityTest.java | 124 ++++
.../apache/sis/internal/jaxb/gml/MeasureTest.java | 2 +-
.../sis/internal/jaxb/gml/TimePeriodTest.java | 2 +-
.../internal/jaxb/lan/FreeTextMarshallingTest.java | 2 +-
.../sis/internal/jaxb/lan/LanguageCodeTest.java | 4 +-
.../sis/internal/jaxb/lan/PT_LocaleTest.java | 12 +-
.../internal/{jaxb => xml}/XmlUtilitiesTest.java | 2 +-
.../java/org/apache/sis/metadata/HashCodeTest.java | 154 ++++
.../apache/sis/metadata/PropertyAccessorTest.java | 23 -
.../java/org/apache/sis/metadata/PrunerTest.java | 4 +-
.../sis/metadata/iso/ImmutableIdentifierTest.java | 2 +-
.../metadata/iso/citation/DefaultCitationTest.java | 54 +-
.../iso/citation/DefaultResponsibilityTest.java | 2 +-
.../constraint/DefaultLegalConstraintsTest.java | 2 +-
.../DefaultRepresentativeFractionTest.java | 8 +-
.../iso/identification/DefaultResolutionTest.java | 4 +-
.../metadata/iso/lineage/DefaultLineageTest.java | 2 +-
.../java/org/apache/sis/test/MetadataAssert.java | 2 +-
.../java/org/apache/sis/test/XMLComparator.java | 2 +-
.../test/java/org/apache/sis/test/XMLTestCase.java | 29 +-
.../FeatureAttributeMock.java} | 32 +-
.../org/apache/sis/test/mock/MetadataMock.java | 2 +-
.../org/apache/sis/test/mock/package-info.java | 5 +-
.../apache/sis/test/suite/MetadataTestSuite.java | 4 +-
.../sis/test/xml/AnnotationConsistencyCheck.java | 7 +-
.../org/apache/sis/test/xml/PackageVerifier.java | 513 -------------
.../org/apache/sis/test/xml/SchemaCompliance.java | 566 --------------
.../apache/sis/util/iso/NameMarshallingTest.java | 2 +-
.../sis/xml/CharSequenceSubstitutionTest.java | 14 +-
.../org/apache/sis/xml/RenameListGenerator.java | 213 ------
.../apache/sis/xml/TransformingNamespacesTest.java | 2 +-
.../apache/sis/metadata/xml/2007/Multiplicity.xml | 56 ++
.../apache/sis/metadata/xml/2016/Multiplicity.xml | 55 ++
.../org/apache/sis/test/suite/package-info.txt | 3 +
core/sis-raster/pom.xml | 16 +-
.../sis/coverage/grid/GridCoordinatesView.java | 136 ++++
.../org/apache/sis/coverage/grid/GridExtent.java | 491 +++++++++++++
.../org/apache/sis/coverage/grid/GridGeometry.java | 815 +++++++++++++++++++++
.../grid/IncompleteGridGeometryException.java | 70 ++
.../apache/sis/coverage/grid/PixelTranslation.java | 366 +++++++++
.../apache/sis/coverage/grid/package-info.java} | 17 +-
.../java/org/apache/sis/image/DefaultIterator.java | 4 +-
.../java/org/apache/sis/image/PixelIterator.java | 4 +-
.../java/org/apache/sis/image/TransferType.java | 4 +-
.../apache/sis/image/WritablePixelIterator.java | 4 +-
.../java/org/apache/sis/image/package-info.java | 4 +-
.../org/apache/sis/internal/raster/Resources.java | 45 +-
.../sis/internal/raster/Resources.properties | 4 +
.../sis/internal/raster/Resources_fr.properties | 4 +
.../apache/sis/internal/raster/package-info.java | 4 +-
.../apache/sis/coverage/grid/GridGeometryTest.java | 190 +++++
.../sis/coverage/grid/PixelTranslationTest.java | 124 ++++
.../org/apache/sis/image/DefaultIteratorTest.java | 4 +-
.../org/apache/sis/test/suite/RasterTestSuite.java | 8 +-
.../referencing/gazetteer/AbstractLocation.java | 2 -
.../gazetteer/MilitaryGridReferenceSystem.java | 2 -
.../sis/geometry/AbstractDirectPosition.java | 3 +-
.../org/apache/sis/geometry/AbstractEnvelope.java | 3 +-
.../org/apache/sis/geometry/DirectPosition1D.java | 3 +-
.../org/apache/sis/geometry/DirectPosition2D.java | 4 +-
.../org/apache/sis/geometry/GeneralEnvelope.java | 12 +-
.../referencing/CC_GeneralOperationParameter.java | 2 +-
.../referencing/PositionalAccuracyConstant.java | 2 -
.../internal/referencing/ServicesForMetadata.java | 32 +-
.../referencing/provider/DatumShiftGridFile.java | 2 -
.../sis/parameter/AbstractParameterDescriptor.java | 1 -
.../java/org/apache/sis/parameter/Parameters.java | 1 -
.../org/apache/sis/parameter/TensorParameters.java | 9 +-
.../sis/referencing/AbstractIdentifiedObject.java | 5 +-
.../apache/sis/referencing/IdentifiedObjects.java | 34 +-
.../apache/sis/referencing/NamedIdentifier.java | 60 +-
.../sis/referencing/cs/DirectionAlongMeridian.java | 2 +-
.../sis/referencing/datum/BursaWolfParameters.java | 2 +-
.../referencing/datum/DefaultVerticalDatum.java | 2 +-
.../sis/referencing/datum/TimeDependentBWP.java | 3 +-
.../factory/AuthorityFactoryIdentifier.java | 2 -
.../factory/ConcurrentAuthorityFactory.java | 4 +-
.../factory/GeodeticAuthorityFactory.java | 2 -
.../referencing/factory/sql/AuthorityCodes.java | 2 -
.../sis/referencing/factory/sql/AxisName.java | 2 -
.../operation/CoordinateOperationRegistry.java | 6 +-
.../operation/DefaultPassThroughOperation.java | 5 +-
.../operation/builder/LinearTransformBuilder.java | 2 -
.../sis/referencing/operation/matrix/Matrix1.java | 2 +-
.../sis/referencing/operation/matrix/Matrix2.java | 2 +-
.../operation/projection/NormalizedProjection.java | 2 +-
.../operation/projection/ZonedGridSystem.java | 2 +-
.../operation/transform/ConcatenatedTransform.java | 2 +-
.../transform/DefaultMathTransformFactory.java | 2 +-
.../transform/EllipsoidToCentricTransform.java | 2 +-
.../transform/ExponentialTransform1D.java | 2 +-
.../operation/transform/LinearTransform1D.java | 3 +-
.../transform/LogarithmicTransform1D.java | 2 +-
.../operation/transform/MathTransforms.java | 45 +-
.../operation/transform/MolodenskyFormula.java | 2 +-
.../operation/transform/PassThroughTransform.java | 64 +-
.../operation/transform/PowerTransform1D.java | 2 +-
.../operation/transform/TransferFunction.java | 2 -
.../datum/DefaultPrimeMeridianTest.java | 2 +-
.../datum/DefaultVerticalDatumTest.java | 2 +-
.../transform/ConcatenatedTransformTest.java | 2 +-
.../operation/transform/MathTransformsTest.java | 2 +-
.../transform/PassThroughTransformTest.java | 8 +-
.../transform/TransformSeparatorTest.java | 2 +-
.../apache/sis/test/integration/MetadataTest.java | 2 +-
.../apache/sis/internal/converter/ClassPair.java | 2 -
.../sis/internal/converter/ConverterRegistry.java | 2 -
.../sis/internal/converter/FallbackConverter.java | 2 -
.../internal/converter/SurjectiveConverter.java | 2 -
.../org/apache/sis/internal/system/Supervisor.java | 2 +-
.../apache/sis/internal/util/AbstractMapEntry.java | 2 -
.../java/org/apache/sis/internal/util/Cloner.java | 33 +-
.../org/apache/sis/internal/util/DoubleDouble.java | 2 +-
.../apache/sis/internal/util/FinalFieldSetter.java | 157 ++++
.../org/apache/sis/internal/util/Numerics.java | 10 -
.../sis/internal/util/TemporalUtilities.java | 2 +-
.../org/apache/sis/internal/util/Utilities.java | 2 +-
.../src/main/java/org/apache/sis/math/Line.java | 2 +-
.../src/main/java/org/apache/sis/math/Plane.java | 2 +-
.../java/org/apache/sis/measure/AngleFormat.java | 2 -
.../org/apache/sis/measure/LinearConverter.java | 8 +-
.../java/org/apache/sis/measure/RangeFormat.java | 20 +-
.../java/org/apache/sis/measure/UnitFormat.java | 21 +-
.../java/org/apache/sis/util/CharSequences.java | 2 +-
.../apache/sis/util/CorruptedObjectException.java | 13 +-
.../src/main/java/org/apache/sis/util/Debug.java | 20 +-
.../main/java/org/apache/sis/util/Exceptions.java | 4 +
.../apache/sis/util/logging/WarningListeners.java | 8 +-
.../java/org/apache/sis/util/resources/Errors.java | 15 +
.../apache/sis/util/resources/Errors.properties | 3 +
.../apache/sis/util/resources/Errors_fr.properties | 5 +-
.../sis/util/resources/IndexedResourceBundle.java | 1 -
.../org/apache/sis/util/resources/Vocabulary.java | 17 +-
.../sis/util/resources/Vocabulary.properties | 3 +
.../sis/util/resources/Vocabulary_fr.properties | 3 +
.../org/apache/sis/internal/util/CitationMock.java | 2 -
ide-project/NetBeans/nbproject/genfiles.properties | 2 +-
ide-project/NetBeans/nbproject/project.xml | 1 +
pom.xml | 4 +-
.../sis/internal/profile/fra/Constraints.java | 1 -
.../internal/profile/fra/DataIdentification.java | 1 -
.../sis/internal/profile/fra/LegalConstraints.java | 1 -
.../internal/profile/fra/SecurityConstraints.java | 1 -
.../sis/storage/earthobservation/LandsatStore.java | 2 -
.../org/apache/sis/internal/geotiff/Resources.java | 5 +
.../sis/internal/geotiff/Resources.properties | 1 +
.../sis/internal/geotiff/Resources_fr.properties | 1 +
.../org/apache/sis/storage/geotiff/CRSBuilder.java | 148 ++--
.../org/apache/sis/storage/geotiff/GeoCodes.java | 8 +-
.../sis/storage/geotiff/GridGeometryBuilder.java | 390 ++++++++++
.../sis/storage/geotiff/ImageFileDirectory.java | 191 ++---
.../{GridGeometry.java => Localization.java} | 123 +---
.../java/org/apache/sis/storage/geotiff/Type.java | 259 ++++---
.../org/apache/sis/storage/geotiff/TypeTest.java | 23 +-
.../java/org/apache/sis/internal/netcdf/Axis.java | 29 +-
.../org/apache/sis/internal/netcdf/Decoder.java | 25 +-
.../apache/sis/internal/netcdf/GridGeometry.java | 7 +-
.../org/apache/sis/internal/netcdf/Variable.java | 2 -
.../sis/internal/netcdf/impl/ChannelDecoder.java | 2 -
.../apache/sis/internal/netcdf/impl/Dimension.java | 2 -
.../sis/internal/netcdf/impl/GridGeometryInfo.java | 2 +-
.../sis/internal/netcdf/impl/package-info.java | 2 +-
.../apache/sis/internal/netcdf/package-info.java | 6 +-
.../sis/internal/netcdf/ucar/DecoderWrapper.java | 2 -
.../sis/internal/netcdf/ucar/package-info.java | 7 +-
.../apache/sis/storage/netcdf/MetadataReader.java | 179 +++--
.../org/apache/sis/storage/netcdf/NetcdfStore.java | 2 -
.../apache/sis/storage/netcdf/package-info.java | 2 +-
.../internal/netcdf/impl/ChannelDecoderTest.java | 3 +-
.../org/apache/sis/test/suite/package-info.txt | 3 +
storage/sis-storage/pom.xml | 5 +
.../sis/internal/storage/MetadataBuilder.java | 92 ++-
.../sis/internal/storage/StoreUtilities.java | 2 +-
.../sis/internal/storage/io/ChannelData.java | 2 -
.../sis/internal/storage/io/IOUtilities.java | 6 +-
.../sis/internal/storage/xml/StoreProvider.java | 2 +-
.../org/apache/sis/storage/DataStoreRegistry.java | 133 ++--
.../apache/sis/storage/GridCoverageResource.java | 21 +-
.../org/apache/sis/storage/ProbeProviderPair.java | 5 +-
.../java/org/apache/sis/storage/ProbeResult.java | 2 -
.../org/apache/sis/storage/StorageConnector.java | 2 -
.../internal/storage/xml/MimeTypeDetectorTest.java | 2 +-
.../apache/sis/internal/storage/xml/StoreTest.java | 2 +-
.../storage/xml/stream/FormattedWriter.java | 1 +
.../storage/xml/stream/NamespaceEraser.java | 1 +
.../internal/storage/xml/stream/StaxDataStore.java | 2 -
.../internal/storage/xml/stream/package-info.java | 2 +-
.../sis/internal/storage/gpx/WriterTest.java | 2 -
451 files changed, 8121 insertions(+), 3758 deletions(-)
diff --cc application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/FeatureTable.java
index 0000000,fa5338a..dd5ec20
mode 000000,100644..100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/FeatureTable.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/FeatureTable.java
@@@ -1,0 -1,169 +1,169 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.gui.dataset;
+
+ import java.util.Map;
+ import java.util.HashMap;
+ import java.util.List;
+ import java.util.Iterator;
+ import java.util.ResourceBundle;
+ import java.util.MissingResourceException;
+ import java.util.stream.Stream;
+ import java.util.stream.Collectors;
+ import javafx.collections.FXCollections;
+ import javafx.scene.control.Label;
+ import javafx.scene.control.ScrollPane;
+ import javafx.scene.control.TableColumn;
+ import javafx.scene.control.TableView;
+ import javafx.scene.control.TreeItem;
+ import javafx.scene.control.TreeView;
+ import javafx.scene.layout.BorderPane;
+ import javafx.beans.property.SimpleObjectProperty;
-import org.opengis.feature.Feature;
-import org.opengis.feature.PropertyType;
++import org.apache.sis.feature.AbstractFeature;
++import org.apache.sis.feature.AbstractIdentifiedType;
+ import org.opengis.geometry.Geometry;
+ import org.apache.sis.internal.util.CheckedArrayList;
+ import org.apache.sis.storage.DataStoreException;
+ import org.apache.sis.storage.FeatureSet;
+ import org.apache.sis.storage.Resource;
+
+
+ /**
+ *
+ * @author Johann Sorel (Geomatys)
+ * @author Smaniotto Enzo
+ * @version 1.0
+ * @since 1.0
+ * @module
+ */
+ public class FeatureTable extends BorderPane {
+ /**
+ * Contains ResourceBundles indexed by table names.
+ */
+ private final Map<String, ResourceBundle> bundles = new HashMap<>();
+
+ private String bundlePrefix;
+
- private String generateFinalColumnName(final PropertyType prop) {
++ private String generateFinalColumnName(final AbstractIdentifiedType prop) {
+ Map<String, Map.Entry<String, String>> labelInfo = (Map) prop.getDesignation();
+ final String labelName = prop.getName().toString();
+ String columnName = labelName;
+ String tableName = null;
+ /*
+ * If exists, explore labelInfo to retrive table and column respect to this label.
+ */
+ if (labelInfo != null) {
+ final Map.Entry<String, String> entry = labelInfo.get(labelName);
+ if (entry != null) {
+ if (entry.getKey() != null) {
+ tableName = entry.getKey();
+ } else {
+ tableName = null;
+ }
+ if (entry.getValue() != null) {
+ columnName = entry.getValue();
+ } else {
+ columnName = labelName;
+ }
+ }
+ }
+ /*
+ * If table name is not null, try to found resourcebundle for this table.
+ */
+ if (tableName != null) {
+ /*
+ * If there isn't resource bundles (or not for the curruen table), try to generate.
+ */
+ if (bundles.get(tableName) == null) {
+ if (bundlePrefix != null) {
+ bundles.put(tableName, ResourceBundle.getBundle(bundlePrefix + tableName));
+ }
+ }
+ }
+ final ResourceBundle bundle = bundles.get(tableName);
+ String finalColumnName;
+ if (labelName == null) {
+ finalColumnName = "";
+ } else if (bundle == null) {
+ if (!labelName.equals(columnName)) {
+ finalColumnName = columnName + " as " + labelName;
+ } else {
+ finalColumnName = columnName;
+ }
+ } else {
+ try {
+ if (!labelName.equals(columnName)) {
+ finalColumnName = bundle.getString(columnName) + " as " + labelName;
+ } else {
+ finalColumnName = bundle.getString(columnName);
+ }
+ } catch (MissingResourceException ex) {
+ if (!labelName.equals(columnName)) {
+ finalColumnName = columnName + " as " + labelName;
+ } else {
+ finalColumnName = columnName;
+ }
+ }
+ }
+ return finalColumnName;
+ }
+
+ public FeatureTable(Resource res, int i) throws DataStoreException {
- TableView<Feature> ttv = new TableView<>();
++ TableView<AbstractFeature> ttv = new TableView<>();
+ final ScrollPane scroll = new ScrollPane(ttv);
+ scroll.setFitToHeight(true);
+ scroll.setFitToWidth(true);
+ ttv.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY);
+ ttv.setTableMenuButtonVisible(true);
+ ttv.setFixedCellSize(100);
+ scroll.setPrefSize(600, 400);
+ scroll.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
+ setCenter(scroll);
- final List<Feature> list;
++ final List<AbstractFeature> list;
+ if (res instanceof FeatureSet) {
- try (Stream<Feature> stream = ((FeatureSet) res).features(false)) {
++ try (Stream<AbstractFeature> stream = ((FeatureSet) res).features(false)) {
+ list = stream.collect(Collectors.toList());
+ ttv.setItems(FXCollections.observableArrayList(list));
- for (PropertyType pt : list.get(0).getType().getProperties(false)) {
- final TableColumn<Feature, BorderPane> column = new TableColumn<>(generateFinalColumnName(pt));
- column.setCellValueFactory((TableColumn.CellDataFeatures<Feature, BorderPane> param) -> {
++ for (AbstractIdentifiedType pt : list.get(0).getType().getProperties(false)) {
++ final TableColumn<AbstractFeature, BorderPane> column = new TableColumn<>(generateFinalColumnName(pt));
++ column.setCellValueFactory((TableColumn.CellDataFeatures<AbstractFeature, BorderPane> param) -> {
+ final Object val = param.getValue().getPropertyValue(pt.getName().toString());
+ if (val instanceof Geometry) {
+ return new SimpleObjectProperty<>(new BorderPane(new Label("{geometry}")));
+ } else {
+ SimpleObjectProperty<BorderPane> sop = new SimpleObjectProperty<>();
+ if (val instanceof CheckedArrayList<?>) {
+ Iterator<String> it = ((CheckedArrayList<String>) val).iterator();
+ TreeItem<String> ti = new TreeItem<>(it.next());
+ while (it.hasNext()) {
+ ti.getChildren().add(new TreeItem<>(it.next()));
+ }
+ BorderPane bp = new BorderPane(new TreeView<>(ti));
+ sop.setValue(bp);
+ return sop;
+ } else {
+ sop.setValue(new BorderPane(new Label(String.valueOf(val))));
+ return sop;
+ }
+ }
+ });
+ ttv.getColumns().add(column);
+ }
+ }
+ }
+ }
+ }
diff --cc application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataOverview.java
index 0000000,77df9a5..b556230
mode 000000,100644..100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataOverview.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataOverview.java
@@@ -1,0 -1,430 +1,403 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.gui.metadata;
+
+ import java.io.IOException;
+ import java.io.InputStream;
+ import java.text.DateFormat;
+ import java.text.NumberFormat;
+ import java.util.Collection;
+ import java.util.HashMap;
+ import java.util.Iterator;
+ import java.util.Locale;
+ import java.util.Map;
+ import javafx.beans.value.ObservableValue;
+ import javafx.collections.FXCollections;
+ import javafx.collections.ObservableList;
+ import javafx.event.ActionEvent;
+ import javafx.event.Event;
+ import javafx.geometry.Insets;
+ import javafx.scene.Node;
+ import javafx.scene.canvas.Canvas;
+ import javafx.scene.control.ComboBox;
+ import javafx.scene.control.Label;
+ import javafx.scene.control.TitledPane;
+ import javafx.scene.control.Toggle;
+ import javafx.scene.control.ToggleButton;
+ import javafx.scene.control.ToggleGroup;
+ import javafx.scene.image.Image;
+ import javafx.scene.layout.GridPane;
+ import javafx.scene.layout.HBox;
+ import javafx.scene.layout.Priority;
+ import javafx.scene.layout.StackPane;
+ import javafx.scene.layout.VBox;
+ import javafx.scene.paint.Color;
+ import org.opengis.metadata.Metadata;
+ import org.opengis.metadata.citation.CitationDate;
-import org.opengis.metadata.citation.Party;
-import org.opengis.metadata.citation.Responsibility;
+ import org.opengis.metadata.extent.Extent;
+ import org.opengis.metadata.extent.GeographicBoundingBox;
+ import org.opengis.metadata.extent.GeographicDescription;
+ import org.opengis.metadata.extent.GeographicExtent;
+ import org.opengis.metadata.identification.DataIdentification;
+ import org.opengis.metadata.identification.Identification;
+ import org.opengis.metadata.identification.TopicCategory;
+ import org.opengis.metadata.spatial.Dimension;
+ import org.opengis.metadata.spatial.SpatialRepresentation;
+ import org.opengis.metadata.spatial.SpatialRepresentationType;
+ import org.opengis.referencing.ReferenceSystem;
+ import org.opengis.util.InternationalString;
+ import org.apache.sis.metadata.iso.DefaultMetadata;
-import org.apache.sis.metadata.iso.citation.DefaultIndividual;
-import org.apache.sis.metadata.iso.citation.DefaultOrganisation;
+ import org.apache.sis.metadata.iso.spatial.DefaultGridSpatialRepresentation;
+ import org.apache.sis.util.iso.Types;
+
+
+ /**
+ * Metadata Viewer.
+ *
+ * @author Smaniotto Enzo
+ * @version 1.0
+ * @since 1.0
+ * @module
+ */
+ class MetadataOverview extends StackPane {
+
+ private final Metadata metadata;
+ final String fromFile;
+ private final Locale locale = Locale.getDefault();
+
+ public MetadataOverview(final DefaultMetadata md, final String fromFile) {
+ this.metadata = md;
+ this.fromFile = fromFile;
+ VBox root = new VBox();
+ root.setStyle("-fx-background-color: linear-gradient(to bottom right, #aeb7c4, #fafafa);");
+
+ // Creation of the differents views.
+ VBox summaryView = createSummaryView();
+ MetadataNode advancedView = new MetadataNode(md.asTreeTable());
+ advancedView.setMaxHeight(Double.MAX_VALUE);
+ VBox.setVgrow(advancedView, Priority.ALWAYS);
+
+ // Create and configure view selection buttons.
+ ToggleGroup buttonGroup = new ToggleGroup();
+ ToggleButton tb1 = new ToggleButton("Summary");
+ ToggleButton tb2 = new ToggleButton("Advanced");
+ tb1.setStyle("-fx-text-fill: white; -fx-font-family: Arial Narrow;-fx-font-weight: bold; -fx-background-color: linear-gradient(#61a2b1, #2A5058); -fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.6) , 5, 0.0 , 0 , 1 ); -fx-padding: 0.8em;");
+ tb2.setStyle("-fx-text-fill: white; -fx-font-family: Arial Narrow;-fx-font-weight: bold; -fx-background-color: linear-gradient(#61a2b1, #2A5058); -fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.6) , 5, 0.0 , 0 , 1 ); -fx-padding: 0.8em;");
+
+ tb1.setToggleGroup(buttonGroup);
+ tb1.setSelected(true);
+ tb1.setDisable(true);
+ tb2.setToggleGroup(buttonGroup);
+ buttonGroup.selectToggle(tb1);
+ buttonGroup.selectedToggleProperty().addListener((ObservableValue<? extends Toggle> observable, Toggle oldValue, Toggle newValue) -> {
+ if (tb2.isSelected()) {
+ tb2.setDisable(true);
+ root.getChildren().remove(summaryView);
+ root.getChildren().add(advancedView);
+ tb1.setDisable(false);
+ } else {
+ tb1.setDisable(true);
+ root.getChildren().add(summaryView);
+ root.getChildren().remove(advancedView);
+ tb2.setDisable(false);
+ }
+ });
+
+ HBox toggleGroupLayout = new HBox();
+ toggleGroupLayout.getChildren().addAll(tb1, tb2);
+ toggleGroupLayout.setPadding(new Insets(0, 0, 10, 0));
+
+ root.getChildren().add(toggleGroupLayout);
+ root.getChildren().add(summaryView);
+
+ this.getChildren().add(root);
+ }
+
+ private VBox createSummaryView() {
+ VBox vb = new VBox();
+ TitledPane idPane = new TitledPane("Identification info", createIdGridPane());
+ GridPane createSpatialGridPane = createSpatialGridPane();
+ vb.getChildren().add(idPane);
+ if (!createSpatialGridPane.getChildren().isEmpty()) {
+ TitledPane spatialPane = new TitledPane("Spatial representation", createSpatialGridPane);
+ vb.getChildren().add(spatialPane);
+ }
+ return vb;
+ }
+
+ private GridPane createIdGridPane() {
+ GridPane gp = new GridPane();
+ gp.setHgap(10.00);
+ int j = 0, k = 1;
+
+ HashMap<String, Identification> m = new HashMap<>();
+ ComboBox<String> comboBox = createComboBox(m);
+ comboBox.setStyle("-fx-font-weight: bold; -fx-font-size: 2em;");
+ if (!comboBox.isVisible()) {
+ Label la = new Label(comboBox.getValue());
+ la.setStyle("-fx-font-weight: bold; -fx-font-size: 2em;");
+ gp.add(la, j, k++, 2, 1);
+ } else {
+ gp.add(comboBox, j, k++, 2, 1);
+ }
+
- // Show author information.
- Collection<? extends Responsibility> contacts = this.metadata.getContacts();
- if (!contacts.isEmpty()) {
- Responsibility contact = contacts.iterator().next();
- Collection<? extends Party> parties = contact.getParties();
- if (!parties.isEmpty()) {
- Party party = parties.iterator().next();
- if (party.getName() != null) {
- Label partyType = new Label("Party");
- Label partyValue = new Label(party.getName().toString());
- partyValue.setWrapText(true);
- if (party instanceof DefaultOrganisation) {
- partyType.setText("Organisation");
- } else if (party instanceof DefaultIndividual) {
- partyType.setText("Author");
- }
- gp.add(partyType, j, k);
- gp.add(partyValue, ++j, k++);
- j = 0;
- }
- }
- }
-
+ GridPane gpi = new GridPane();
+ gpi.setHgap(10.00);
+
+ comboBox.setOnAction(e -> {
+ gpi.getChildren().clear();
- Identification id = m.get(comboBox.getValue());
++ DataIdentification id = (DataIdentification) m.get(comboBox.getValue());
+ if (comboBox.getValue().equals("No data to show")) {
+ return;
+ }
+
+ // Show the abstract or the credit, the topic category, the creation date, the type of data, the representation system info and also the geographical area.
+ Object ab = id.getAbstract();
+ if (ab != null) {
+ InternationalString abs = (InternationalString) ab;
+ Label crd = new Label("Abstract");
+ Label crdValue = new Label(abs.toString(locale));
+ crdValue.setWrapText(true);
+ gpi.add(crd, 0, 1);
+ gpi.add(crdValue, 1, 1);
+ } else {
+ Collection<String> credits = id.getCredits();
+ if (!credits.isEmpty()) {
+ String credit = credits.iterator().next();
+ Label crd = new Label("Credit");
+ Label crdValue = new Label(credit.toString());
+ crdValue.setWrapText(true);
+ gpi.add(crd, 0, 1);
+ gpi.add(crdValue, 1, 1);
+ }
+ }
+
+ Collection<TopicCategory> tcs = id.getTopicCategories();
+ if (!tcs.isEmpty()) {
+ TopicCategory tc = tcs.iterator().next();
+ Label topicC = new Label("Topic Category");
+ Label topicValue = new Label(tc.toString());
+ topicValue.setWrapText(true);
+ gpi.add(topicC, 0, 2);
+ gpi.add(topicValue, 1, 2);
+ }
+
+ if (!id.getCitation().getDates().isEmpty()) {
+ CitationDate dateAndType = id.getCitation().getDates().iterator().next();
+ DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.MEDIUM, locale);
+ String dateStr = dateFormat.format(dateAndType.getDate());
+ String s = dateAndType.getDateType().toString();
+ s = s.replace("DateType[", "");
+ s = s.replace("]", "");
+ Label dt = new Label("Date type: " + s.toLowerCase());
+ Label dtValue = new Label(dateStr);
+ dtValue.setWrapText(true);
+ gpi.add(dt, 0, 3);
+ gpi.add(dtValue, 1, 3);
+ }
+
+ if (id instanceof DataIdentification) {
+ Label topicC = new Label("Object type");
+ Label topicValue = new Label("Data");
+ topicValue.setWrapText(true);
+ gpi.add(topicC, 0, 4);
+ gpi.add(topicValue, 1, 4);
+ } else {
+ Label topicC = new Label("Object type");
+ Label topicValue = new Label("Service");
+ topicValue.setWrapText(true);
+ gpi.add(topicC, 0, 4);
+ gpi.add(topicValue, 1, 4);
+ }
+
+ Collection<SpatialRepresentationType> spatialRepresentationTypes = id.getSpatialRepresentationTypes();
+ Iterator<SpatialRepresentationType> its = spatialRepresentationTypes.iterator();
+ String typeList = "Spatial representation type: ";
+ while (its.hasNext()) {
+ SpatialRepresentationType spatialRepresentationType = its.next();
+ typeList += spatialRepresentationType.toString().toLowerCase(locale).replace("spatialrepresentationtype[", "").replace(']', '\0') + ", ";
+ }
+ if (!typeList.equals("Spatial representation type: ")) {
+ Label list = new Label(typeList.substring(0, typeList.length() - 2));
+ list.setWrapText(true);
+ gpi.add(list, 0, 5, 2, 1);
+ }
+
+ Collection<? extends Extent> exs = id.getExtents();
+ if (!exs.isEmpty()) {
+ Extent ex = exs.iterator().next();
+ Collection<? extends GeographicExtent> ges = ex.getGeographicElements();
+ Iterator<? extends GeographicExtent> it = ges.iterator();
+ while (it.hasNext()) {
+ GeographicExtent ge = it.next();
+ Label geoEx = new Label("Zone");
+ Label geoExValue = new Label(ge.toString());
+ geoExValue.setWrapText(true);
+ if (ge instanceof GeographicBoundingBox) {
+ geoEx.setText("");
+ GeographicBoundingBox gbd = (GeographicBoundingBox) ge;
+ geoExValue.setText("");
+ Canvas c = createMap(gbd.getNorthBoundLatitude(), gbd.getEastBoundLongitude(), gbd.getSouthBoundLatitude(), gbd.getWestBoundLongitude());
+ if (c != null) {
+ gpi.add(c, 0, 6, 2, 1);
+ } else {
+ geoEx.setText("Impossible to load the map.");
+ gpi.add(geoEx, 0, 6);
+ gpi.add(geoExValue, 1, 6);
+ }
+ } else if (ge instanceof GeographicDescription) {
+ geoEx.setText("Geographic description");
+ GeographicDescription gd = (GeographicDescription) ge;
+ geoExValue.setText(gd.getGeographicIdentifier().getCode());
+ }
+ }
+ }
+ });
+
+ Event.fireEvent(comboBox, new ActionEvent());
+ gp.add(gpi, j, k++, 2, 1);
+
+ int ind = 0;
+ for (Node n : gp.getChildren()) {
+ if (ind++ != 0) {
+ n.setStyle("-fx-padding: 0 83 10 0;");
+ } else {
+ n.setStyle("-fx-padding: 0 0 10 0; -fx-font-weight: bold; -fx-font-size: 2em;");
+ }
+ }
+ gpi.getChildren().forEach(n -> n.setStyle("-fx-padding: 0 0 10 0;"));
+
+ return gp;
+ }
+
+ private Canvas createMap(double north, double east, double south, double west) {
+ Canvas can = new Canvas();
+ Image image = null;
+ try (InputStream in = MetadataOverview.class.getResourceAsStream("WorldMap360x180.png")) {
+ image = new Image(in);
+ } catch (IOException e) {
+ // TODO
+ }
+ if (image.errorProperty().getValue()) {
+ return null;
+ }
+
+ double height = image.getHeight();
+ double width = image.getWidth();
+
+ can.setHeight(height);
+ can.setWidth(width);
+ can.getGraphicsContext2D().drawImage(image, 0, 0, width, height);
+ can.getGraphicsContext2D().setStroke(Color.DARKBLUE);
+ can.getGraphicsContext2D().setGlobalAlpha(0.1);
+ double x = west + width / 2, y = height / 2 - north, w = east - west, h = north - south;
+ can.getGraphicsContext2D().strokeRect(x, y, w, h);
+ final double minRectSize = 6.0;
+ if (w < minRectSize) {
+ double difX = minRectSize - w;
+ x -= difX / 2;
+ w = minRectSize;
+ }
+ if (h < minRectSize) {
+ double difY = minRectSize - h;
+ y -= difY / 2;
+ h = minRectSize;
+ }
+ can.getGraphicsContext2D().fillRect(x, y, w, h);
+ can.getGraphicsContext2D().setGlobalAlpha(1.0);
+ can.getGraphicsContext2D().setStroke(Color.DARKBLUE);
+ can.getGraphicsContext2D().strokeRect(x, y, w, h);
+
+ return can;
+ }
+
+ private GridPane createSpatialGridPane() {
+ GridPane gp = new GridPane();
+ gp.setHgap(10.00);
+ gp.setVgap(10.00);
+ int j = 0, k = 1;
+
+ Collection<? extends ReferenceSystem> referenceSystemInfos = metadata.getReferenceSystemInfo();
+ if (!referenceSystemInfos.isEmpty()) {
+ ReferenceSystem referenceSystemInfo = referenceSystemInfos.iterator().next();
+ Label rsiValue = new Label("Reference system infos: " + referenceSystemInfo.getName().toString());
+ rsiValue.setWrapText(true);
+ gp.add(rsiValue, j, k++);
+ }
+
+ Collection<? extends SpatialRepresentation> sris = this.metadata.getSpatialRepresentationInfo();
+ if (sris.isEmpty()) {
+ return gp;
+ }
+ NumberFormat numberFormat = NumberFormat.getIntegerInstance(locale);
+ for (SpatialRepresentation sri : sris) {
+ String currentValue = "• ";
+ if (sri instanceof DefaultGridSpatialRepresentation) {
+ DefaultGridSpatialRepresentation sr = (DefaultGridSpatialRepresentation) sri;
+
+ Iterator<? extends Dimension> it = sr.getAxisDimensionProperties().iterator();
+ while (it.hasNext()) {
+ Dimension dim = it.next();
+ currentValue += numberFormat.format(dim.getDimensionSize()) + " " + Types.getCodeTitle(dim.getDimensionName()) + " * ";
+ }
+ currentValue = currentValue.substring(0, currentValue.length() - 3);
+ Label spRep = new Label(currentValue);
+ gp.add(spRep, j, k++, 2, 1);
+ if (sr.getCellGeometry() != null) {
+ Label cellGeo = new Label("Cell geometry:");
+ Label cellGeoValue = new Label(Types.getCodeTitle(sr.getCellGeometry()).toString());
+ cellGeoValue.setWrapText(true);
+ gp.add(cellGeo, j, k);
+ gp.add(cellGeoValue, ++j, k++);
+ j = 0;
+ }
+ }
+ }
+ return gp;
+ }
+
+ private ComboBox<String> createComboBox(final Map<String, Identification> m) {
+ ComboBox<String> cb = new ComboBox<>();
+ Collection<? extends Identification> ids = this.metadata.getIdentificationInfo();
+ ObservableList<String> options = FXCollections.observableArrayList();
+ int i = 1;
+ if (ids.size() > 1) {
+ for (Identification id : ids) {
+ String currentName;
+ if (id.getCitation() != null) {
+ currentName = id.getCitation().getTitle().toString();
+ } else {
+ currentName = Integer.toString(i);
+ }
+ options.add(currentName);
+ m.put(currentName, id);
+ }
+ cb.setItems(options);
+ cb.setValue(ids.iterator().next().getCitation().getTitle().toString());
+ } else if (ids.size() == 1) {
+ if (ids.iterator().next().getCitation() != null) {
+ m.put(ids.iterator().next().getCitation().getTitle().toString(), ids.iterator().next());
+ cb.setValue(ids.iterator().next().getCitation().getTitle().toString());
+ cb.setVisible(false);
+ }
+ } else {
+ cb.setValue("No data to show");
+ cb.setVisible(false);
+ }
+ return cb;
+ }
+ }
diff --cc core/sis-feature/src/main/java/org/apache/sis/feature/AbstractOperation.java
index a5959f1,fa35d1a..52ec3d5
--- a/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractOperation.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractOperation.java
@@@ -30,9 -30,16 +30,8 @@@ import org.opengis.parameter.GeneralPar
import org.opengis.parameter.ParameterDescriptorGroup;
import org.opengis.parameter.ParameterValueGroup;
import org.apache.sis.util.Classes;
- import org.apache.sis.util.Debug;
// Branch-dependent imports
-import org.opengis.feature.Attribute;
-import org.opengis.feature.AttributeType;
-import org.opengis.feature.Feature;
-import org.opengis.feature.FeatureAssociation;
-import org.opengis.feature.FeatureOperationException;
-import org.opengis.feature.IdentifiedType;
-import org.opengis.feature.Operation;
-import org.opengis.feature.Property;
/**
diff --cc core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java
index 975ccf6,c8e414b..bffabc3
--- a/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/feature/PropertyView.java
@@@ -25,9 -25,17 +25,8 @@@ import org.opengis.util.GenericName
import org.apache.sis.util.collection.CheckedContainer;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.Classes;
- import org.apache.sis.util.Debug;
import org.apache.sis.internal.feature.Resources;
-// Branch-dependent imports
-import org.opengis.feature.Feature;
-import org.opengis.feature.Property;
-import org.opengis.feature.Operation;
-import org.opengis.feature.PropertyType;
-import org.opengis.feature.AttributeType;
-import org.opengis.feature.FeatureAssociationRole;
-import org.opengis.feature.MultiValuedPropertyException;
-
/**
* An attribute or association implementation which delegate its work to the parent feature.
@@@ -149,11 -189,26 +148,10 @@@ final class PropertyView
}
/**
- * Compares this attribute with the given object for equality.
- */
- @Override
- public final boolean equals(final Object obj) {
- if (obj == this) {
- return true;
- }
- if (obj != null && obj.getClass() == getClass()) {
- final PropertyView<?> that = (PropertyView<?>) obj;
- return feature == that.feature && Objects.equals(name, that.name);
- }
- return false;
- }
-
- /**
* Returns a string representation of this property for debugging purposes.
*/
- @Debug
- @Override
- public final String toString() {
- return FieldType.toString(false, getClass().getSimpleName(), getName(),
- Classes.getShortName(getValueClass()), getValues().iterator()).toString();
+ static String toString(final Class<?> classe, final Class<?> valueClass, final GenericName name, final Collection<?> values) {
+ return FieldType.toString(false, classe.getSimpleName(), name,
+ Classes.getShortName(valueClass), values.iterator()).toString();
}
}
diff --cc core/sis-feature/src/main/java/org/apache/sis/feature/builder/TypeBuilder.java
index 90e2c18,3dffc1b..345db73
--- a/core/sis-feature/src/main/java/org/apache/sis/feature/builder/TypeBuilder.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/feature/builder/TypeBuilder.java
@@@ -32,8 -32,11 +32,7 @@@ import org.apache.sis.util.NullArgument
import org.apache.sis.util.Deprecable;
import org.apache.sis.util.Localized;
import org.apache.sis.util.Classes;
- import org.apache.sis.util.Debug;
-// Branch-dependent imports
-import org.opengis.feature.IdentifiedType;
-import org.opengis.feature.PropertyNotFoundException;
-
/**
* Information common to all kind of types (feature, association, characteristics).
diff --cc core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/cat/CodeListUID.java
index 316281b,05d8053..7570f1d
--- a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/cat/CodeListUID.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/cat/CodeListUID.java
@@@ -23,9 -23,10 +23,9 @@@ import javax.xml.bind.annotation.XmlAtt
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;
import org.opengis.util.CodeList;
-import org.opengis.util.ControlledVocabulary;
import org.apache.sis.util.iso.Types;
import org.apache.sis.internal.jaxb.Context;
- import org.apache.sis.internal.jaxb.Schemas;
+ import org.apache.sis.internal.xml.Schemas;
/**
diff --cc core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/gml/TM_Primitive.java
index 47d05bf,9903cd0..546ced4
--- a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/gml/TM_Primitive.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/gml/TM_Primitive.java
@@@ -18,9 -18,11 +18,9 @@@ package org.apache.sis.internal.jaxb.gm
import java.util.Date;
import javax.xml.bind.annotation.XmlElement;
-import org.opengis.temporal.Period;
-import org.opengis.temporal.Instant;
import org.opengis.temporal.TemporalPrimitive;
+ import org.apache.sis.internal.xml.XmlUtilities;
import org.apache.sis.internal.jaxb.Context;
- import org.apache.sis.internal.jaxb.XmlUtilities;
import org.apache.sis.internal.jaxb.gco.PropertyType;
import org.apache.sis.internal.util.TemporalUtilities;
import org.apache.sis.util.resources.Errors;
diff --cc core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/gml/TimeInstant.java
index 28da4df,611e527..d6164e4
--- a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/gml/TimeInstant.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/gml/TimeInstant.java
@@@ -22,12 -22,10 +22,12 @@@ import javax.xml.bind.annotation.XmlEle
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.datatype.DatatypeConfigurationException;
-import org.opengis.temporal.Instant;
import org.apache.sis.internal.jaxb.Context;
- import org.apache.sis.internal.jaxb.XmlUtilities;
+ import org.apache.sis.internal.xml.XmlUtilities;
+// Branch-dependent imports
+import org.apache.sis.internal.geoapi.temporal.Instant;
+
/**
* Encapsulates a {@code gml:TimeInstant}. This element may be used alone, or included in a
diff --cc core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/gml/TimePeriod.java
index 9addb31,5a66b04..0b8bc63
--- a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/gml/TimePeriod.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/gml/TimePeriod.java
@@@ -20,10 -20,10 +20,10 @@@ import javax.xml.bind.annotation.XmlTyp
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElements;
import javax.xml.bind.annotation.XmlRootElement;
-import org.opengis.temporal.Period;
import org.apache.sis.internal.jaxb.Context;
+import org.apache.sis.internal.geoapi.temporal.Period;
- import static org.apache.sis.internal.jaxb.LegacyNamespaces.VERSION_3_0;
+ import static org.apache.sis.internal.xml.LegacyNamespaces.VERSION_3_0;
/**
diff --cc core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/gts/TM_PeriodDuration.java
index c937985,4d561f8..4e297c8
--- a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/gts/TM_PeriodDuration.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/gts/TM_PeriodDuration.java
@@@ -23,10 -23,10 +23,10 @@@ import javax.xml.datatype.DatatypeFacto
import javax.xml.bind.annotation.XmlElement;
import javax.xml.datatype.DatatypeConfigurationException;
import org.opengis.temporal.PeriodDuration;
-import org.opengis.temporal.TemporalFactory;
+import org.apache.sis.internal.geoapi.temporal.TemporalFactory;
import org.opengis.util.InternationalString;
import org.apache.sis.internal.jaxb.Context;
- import org.apache.sis.internal.jaxb.XmlUtilities;
+ import org.apache.sis.internal.xml.XmlUtilities;
import org.apache.sis.internal.jaxb.gco.PropertyType;
import org.apache.sis.internal.util.TemporalUtilities;
import org.apache.sis.util.iso.SimpleInternationalString;
diff --cc core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/metadata/replace/package-info.java
index a21ec8b,16aece8..8c315ac
--- a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/metadata/replace/package-info.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/metadata/replace/package-info.java
@@@ -59,6 -60,7 +59,6 @@@ import javax.xml.bind.annotation.XmlAcc
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;
- import org.apache.sis.internal.jaxb.LegacyNamespaces;
+ import org.apache.sis.internal.xml.LegacyNamespaces;
-import org.apache.sis.internal.jaxb.code.SV_ParameterDirection;
import org.apache.sis.internal.jaxb.gco.*;
import org.apache.sis.xml.Namespaces;
diff --cc core/sis-metadata/src/main/java/org/apache/sis/internal/simple/SimpleFormat.java
index 115a66f,3e68625..94908a5
--- a/core/sis-metadata/src/main/java/org/apache/sis/internal/simple/SimpleFormat.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/simple/SimpleFormat.java
@@@ -18,10 -18,11 +18,9 @@@ package org.apache.sis.internal.simple
import java.util.Collection;
import java.util.Collections;
- import org.apache.sis.util.Debug;
import org.opengis.util.InternationalString;
-import org.opengis.metadata.citation.Citation;
import org.opengis.metadata.distribution.Distributor;
import org.opengis.metadata.distribution.Format;
-import org.opengis.metadata.distribution.Medium;
/**
diff --cc core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java
index 928f7be,69ca60e..04840de
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java
@@@ -30,8 -31,6 +31,7 @@@ import java.security.AccessController
import org.opengis.metadata.Identifier;
import org.opengis.metadata.citation.Citation;
import org.opengis.metadata.ExtendedElementInformation;
+import org.opengis.referencing.ReferenceIdentifier;
- import org.apache.sis.util.Debug;
import org.apache.sis.util.Classes;
import org.apache.sis.util.ComparisonMode;
import org.apache.sis.util.collection.TreeTable;
diff --cc core/sis-metadata/src/main/java/org/apache/sis/metadata/Pruner.java
index 898a5ea,b2fbc85..801eee0
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/Pruner.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/Pruner.java
@@@ -16,10 -16,9 +16,9 @@@
*/
package org.apache.sis.metadata;
- import java.util.Map;
import java.util.Iterator;
import java.util.Collection;
-import org.opengis.util.ControlledVocabulary;
+import org.opengis.util.CodeList;
import org.apache.sis.util.Emptiable;
import org.apache.sis.internal.util.CollectionsExt;
@@@ -110,128 -89,106 +89,106 @@@ final class Pruner extends MetadataVisi
}
/**
- * {@link #isEmpty(AbstractMetadata, Class, boolean, boolean)} implementation, potentially
- * invoked recursively for inspecting child metadata and optionally removing empty ones.
- * The map given in argument is a safety guard against infinite recursivity.
+ * Marks a metadata instance as empty before we start visiting its non-null properties.
+ * If the metadata does not contain any property, then the {@link #isEmpty} field will stay {@code true}.
*
- * @param accessor the accessor that provided the metadata {@code properties}.
- * @param properties the metadata properties.
- * @param tested an initially singleton map, to be filled with tested metadata.
- * @param prune {@code true} for removing empty properties.
- * @return {@code true} if all metadata properties are null or empty.
+ * @return {@link Filter#NON_EMPTY} since this visitor is not restricted to writable properties.
+ * We need to visit all readable properties even for pruning operation since we need to
+ * determine if the metadata is empty.
*/
- private static boolean isEmpty(final PropertyAccessor accessor, final Map<String,Object> properties,
- final Map<Object,Boolean> tested, final boolean prune)
- {
- boolean isEmpty = true;
- for (final Map.Entry<String,Object> entry : properties.entrySet()) {
- final Object value = entry.getValue();
- /*
- * No need to check for null values, because the ValueExistencePolicy argument
- * given to asMap(…) asked for non-null values. If nevertheless a value is null,
- * following code should be robust to that.
- *
- * We use the 'tested' map in order to avoid computing the same value twice, but
- * also as a check against infinite recursivity - which is why a value needs to be
- * set before to iterate over children. The default value is 'false' because if we
- * test the same object through a "A → B → A" dependency chain, this means that A
- * was not empty (since it contains B).
- */
- final Boolean isEntryEmpty = tested.put(value, Boolean.FALSE);
- if (isEntryEmpty != null) {
- if (isEntryEmpty) { // If a value was already set, restore the original value.
- tested.put(value, Boolean.TRUE);
- } else {
- isEmpty = false;
- if (!prune) break; // No need to continue if we are not pruning the metadata.
- }
- } else {
+ @Override
+ Filter preVisit(final PropertyAccessor accessor) {
+ isEmpty = true;
+ return Filter.NON_EMPTY;
+ }
+
+ /**
+ * Invoked for each element in the metadata to test or prune. This method is invoked only for new elements
+ * not yet processed by {@code Pruner}. The element may be a value object or a collection. For convenience
+ * we will proceed as if we had only collections, wrapping value object in a singleton collection.
+ *
+ * @param type the type of elements. Note that this is not necessarily the type
+ * of given {@code element} argument if the later is a collection.
+ * @param value value of the metadata element being visited.
+ */
+ @Override
+ Object visit(final Class<?> type, final Object value) {
+ final boolean isEmptyMetadata = isEmpty; // Save the value in case it is overwritten by recursive invocations.
+ boolean isEmptyValue = true;
+ final Collection<?> values = CollectionsExt.toCollection(value);
+ for (final Iterator<?> it = values.iterator(); it.hasNext();) {
+ final Object element = it.next();
+ if (!isNullOrEmpty(element)) {
/*
- * At this point, 'value' is a new instance not yet processed by Pruner. The value may
- * be a data object or a collection. For convenience we will proceed as if we had only
- * collections, wrapping data object in a singleton collection if necessary.
+ * At this point, 'element' is not an empty CharSequence, Collection or array.
+ * It may be an other metadata, a Java primitive type or user-defined object.
+ *
+ * - For AbstractMetadata, delegate to the public API in case it has been overriden.
+ * - For user-defined Emptiable, delegate to the user's isEmpty() method. Note that
+ * we test at different times depending if 'prune' is true of false.
*/
- Class<?> elementType = null; // To be computed when first needed.
- boolean allElementsAreEmpty = true;
- final Collection<?> values = CollectionsExt.toCollection(value);
- for (final Iterator<?> it = values.iterator(); it.hasNext();) {
- final Object element = it.next();
- if (!isNullOrEmpty(element)) {
+ boolean isEmptyElement = false;
+ if (element instanceof AbstractMetadata) {
+ final AbstractMetadata md = (AbstractMetadata) element;
+ if (prune) md.prune();
+ isEmptyElement = md.isEmpty();
+ } else if (!prune && element instanceof Emptiable) {
+ isEmptyElement = ((Emptiable) element).isEmpty();
+ // If 'prune' is true, we will rather test for Emptiable after our pruning attempt.
- } else if (!(element instanceof ControlledVocabulary)) {
++ } else if (!(element instanceof Enum<?>) && !(element instanceof CodeList<?>)) {
+ final MetadataStandard standard = MetadataStandard.forClass(element.getClass());
+ if (standard != null) {
/*
- * At this point, 'element' is not an empty CharSequence, Collection or array.
- * It may be an other metadata, a Java primitive type or user-defined object.
- *
- * - For AbstractMetadata, delegate to the public API in case it has been overriden.
- * - For user-defined Emptiable, delegate to the user's isEmpty() method. Note that
- * we test at different times depending if 'prune' is true of false.
+ * For implementation that are not subtype of AbstractMetadata but nevertheless
+ * implement some metadata interfaces, we will invoke recursively this method.
*/
- boolean isEmptyElement = false;
- if (element instanceof AbstractMetadata) {
- final AbstractMetadata md = (AbstractMetadata) element;
- if (prune) md.prune();
- isEmptyElement = md.isEmpty();
- } else if (!prune && element instanceof Emptiable) {
- isEmptyElement = ((Emptiable) element).isEmpty();
- // If 'prune' is true, we will rather test for Emptiable after our pruning attempt.
- } else if (!(element instanceof Enum<?>) && !(element instanceof CodeList<?>)) {
- final MetadataStandard standard = MetadataStandard.forClass(element.getClass());
- if (standard != null) {
- /*
- * For implementation that are not subtype of AbstractMetadata but nevertheless
- * implement some metadata interface, we will invoke recursively this method.
- * But since a class may implement more than one interface, we need to get the
- * type of the value returned by the getter method in order to take in account
- * only that type.
- */
- if (elementType == null) {
- elementType = accessor.type(accessor.indexOf(entry.getKey(), false),
- TypeValuePolicy.ELEMENT_TYPE);
- }
- final PropertyAccessor elementAccessor = standard.getAccessor(
- new CacheKey(element.getClass(), elementType), false);
- if (elementAccessor != null) {
- isEmptyElement = isEmpty(elementAccessor, asMap(element, elementAccessor, prune), tested, prune);
- if (!isEmptyElement && element instanceof Emptiable) {
- isEmptyElement = ((Emptiable) element).isEmpty();
- }
- }
- } else if (isPrimitive(entry)) {
- if (value instanceof Number) {
- isEmptyElement = Double.isNaN(((Number) value).doubleValue());
- } else {
- // Typically methods of the kind 'isFooAvailable()'.
- isEmptyElement = Boolean.FALSE.equals(value);
- }
- }
- }
- if (!isEmptyElement) {
- // At this point, we have determined that the property is not empty.
- // If we are not removing empty nodes, there is no need to continue.
- if (!prune) {
- return false;
+ final Boolean r = walk(standard, type, value, false);
+ if (r != null) {
+ isEmptyElement = r;
+ if (!isEmptyElement && element instanceof Emptiable) {
+ isEmptyElement = ((Emptiable) element).isEmpty();
}
- allElementsAreEmpty = false;
- continue;
}
- }
- // Found an empty element. Remove it if we are
- // allowed to do so, then check next elements.
- if (prune && values == value) {
- it.remove();
+ } else if (value instanceof Number) {
+ isEmptyElement = Double.isNaN(((Number) value).doubleValue());
+ } else if (value instanceof Boolean) {
+ // Typically methods of the kind 'isFooAvailable()'.
+ isEmptyElement = !((Boolean) value);
}
}
- // If all elements were empty, set the whole property to 'null'.
- isEmpty &= allElementsAreEmpty;
- if (allElementsAreEmpty) {
- tested.put(value, Boolean.TRUE);
- if (prune) try {
- entry.setValue(null);
- } catch (UnsupportedOperationException e) {
- // Entry is read only - ignore.
+ if (!isEmptyElement) {
+ /*
+ * At this point, we have determined that the property is not empty.
+ * If we are not removing empty nodes, there is no need to continue.
+ */
+ if (!prune) {
+ isEmpty = false;
+ return SKIP_SIBLINGS;
}
+ isEmptyValue = false;
+ continue;
}
}
+ /*
+ * Found an empty element. Remove it if the element is part of a collection,
+ * then move to the next element in the collection (not yet the next property).
+ */
+ if (prune && values == value) {
+ it.remove();
+ }
}
+ /*
+ * If all elements were empty, set the whole property to 'null'.
+ */
+ isEmpty = isEmptyMetadata & isEmptyValue;
+ return isEmptyValue & prune ? null : value;
+ }
+
+ /**
+ * Returns the result of visiting all elements in the metadata.
+ */
+ @Override
+ Boolean result() {
return isEmpty;
}
}
diff --cc core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadata.java
index 98b578e,b16b150..81c3a4f
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadata.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/DefaultMetadata.java
@@@ -366,36 -364,7 +360,36 @@@ public class DefaultMetadata extends IS
applicationSchemaInfo = copyCollection(object.getApplicationSchemaInfo(), ApplicationSchemaInformation.class);
metadataMaintenance = object.getMetadataMaintenance();
acquisitionInformation = copyCollection(object.getAcquisitionInformation(), AcquisitionInformation.class);
- resourceLineages = copyCollection(object.getResourceLineages(), Lineage.class);
+ if (object instanceof DefaultMetadata) {
+ final DefaultMetadata c = (DefaultMetadata) object;
- metadataIdentifier = c.getMetadataIdentifier();
++ identifiers = singleton(c.getMetadataIdentifier(), Identifier.class);
+ parentMetadata = c.getParentMetadata();
+ languages = copyCollection(c.getLanguages(), Locale.class);
+ characterSets = copyCollection(c.getCharacterSets(), Charset.class);
+ metadataScopes = copyCollection(c.getMetadataScopes(), DefaultMetadataScope.class);
+ dateInfo = copyCollection(c.getDateInfo(), CitationDate.class);
+ metadataStandards = copyCollection(c.getMetadataStandards(), Citation.class);
+ metadataProfiles = copyCollection(c.getMetadataProfiles(), Citation.class);
+ alternativeMetadataReferences = copyCollection(c.getAlternativeMetadataReferences(), Citation.class);
+ metadataLinkages = copyCollection(c.getMetadataLinkages(), OnlineResource.class);
+ resourceLineages = copyCollection(c.getResourceLineages(), Lineage.class);
+ } else {
+ setFileIdentifier (object.getFileIdentifier());
+ setParentIdentifier (object.getParentIdentifier());
+ setLanguage (object.getLanguage());
+ setLocales (object.getLocales());
+ setCharacterSet (object.getCharacterSet());
+ setHierarchyLevels (object.getHierarchyLevels());
+ setHierarchyLevelNames (object.getHierarchyLevelNames());
+ setDateStamp (object.getDateStamp());
+ setMetadataStandardName (object.getMetadataStandardName());
+ setMetadataStandardVersion(object.getMetadataStandardVersion());
+ try {
+ setDataSetUri(object.getDataSetUri());
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
}
}
@@@ -444,11 -413,11 +438,11 @@@
*
* @since 0.5
*/
- @Override
@XmlElement(name = "metadataIdentifier")
@XmlJavaTypeAdapter(MD_Identifier.Since2014.class)
+ @UML(identifier="metadataIdentifier", obligation=OPTIONAL, specification=ISO_19115)
public Identifier getMetadataIdentifier() {
- return metadataIdentifier;
+ return super.getIdentifier();
}
/**
diff --cc core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ImmutableIdentifier.java
index 889a097,03dde0f..3a59bb1
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ImmutableIdentifier.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ImmutableIdentifier.java
@@@ -194,18 -189,16 +194,20 @@@ public class ImmutableIdentifier extend
* get the code, codespace, authority and version from the given identifier.
*
* @param identifier the identifier to copy.
+ *
- * @see #castOrCopy(Identifier)
++ * @see #castOrCopy(ReferenceIdentifier)
*/
- public ImmutableIdentifier(final Identifier identifier) {
+ public ImmutableIdentifier(final ReferenceIdentifier identifier) {
ensureNonNull("identifier", identifier);
- code = identifier.getCode();
- codeSpace = identifier.getCodeSpace();
- authority = identifier.getAuthority();
- version = identifier.getVersion();
- description = identifier.getDescription();
+ code = identifier.getCode();
+ codeSpace = identifier.getCodeSpace();
+ authority = identifier.getAuthority();
+ version = identifier.getVersion();
+ if (identifier instanceof DefaultIdentifier) {
+ description = ((DefaultIdentifier) identifier).getDescription();
+ } else {
+ description = null;
+ }
validate(null);
}
diff --cc core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/MetadataScopeAdapter.java
index 63ffa27,66889d0..c7f6e8c
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/MetadataScopeAdapter.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/MetadataScopeAdapter.java
@@@ -63,10 -64,12 +63,10 @@@ abstract class MetadataScopeAdapter<L>
* But if the metadata is not modifiable, then we will need to clone it and replaces the element in
* the collection.
*/
- if (!scope.isModifiable()) {
- if (!(scope instanceof DefaultMetadataScope) ||
- ((DefaultMetadataScope) scope).state() == DefaultMetadataScope.State.FINAL)
- {
++ if (scope.state() == DefaultMetadataScope.State.FINAL) {
scope = new DefaultMetadataScope(scope);
if (elements instanceof List<?>) {
- ((List<MetadataScope>) elements).set(n, scope);
+ ((List<DefaultMetadataScope>) elements).set(n, scope);
} else {
/*
* Not a list. Delete all the remaining parts, substitute the value
diff --cc core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultResponsibleParty.java
index 78b2e3d,a0bf497..0d7df56
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultResponsibleParty.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultResponsibleParty.java
@@@ -56,7 -57,7 +56,6 @@@ import org.apache.sis.internal.metadata
* @since 0.3
* @module
*/
- @SuppressWarnings("CloneableClassWithoutClone") // ModifiableMetadata needs shallow clones.
-@Deprecated
@XmlType(name = "CI_ResponsibleParty_Type", namespace = LegacyNamespaces.GMD, propOrder = {
"individualName",
"organisationName",
diff --cc core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/content/DefaultCoverageDescription.java
index 081ad61,13e22f9..ab014b5
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/content/DefaultCoverageDescription.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/content/DefaultCoverageDescription.java
@@@ -33,14 -34,9 +33,14 @@@ import org.opengis.util.RecordType
import org.apache.sis.internal.metadata.Dependencies;
import org.apache.sis.internal.metadata.LegacyPropertyAdapter;
import org.apache.sis.internal.jaxb.FilterByVersion;
- import org.apache.sis.internal.jaxb.LegacyNamespaces;
+ import org.apache.sis.internal.xml.LegacyNamespaces;
import org.apache.sis.internal.jaxb.metadata.MD_Identifier;
+// Branch-specific imports
+import org.opengis.annotation.UML;
+import static org.opengis.annotation.Obligation.OPTIONAL;
+import static org.opengis.annotation.Specification.ISO_19115;
+
/**
* Information about the content of a grid data cell.
diff --cc core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/content/DefaultFeatureCatalogueDescription.java
index 987b4b5,8398143..bfc40ba
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/content/DefaultFeatureCatalogueDescription.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/content/DefaultFeatureCatalogueDescription.java
@@@ -25,8 -25,9 +25,8 @@@ import javax.xml.bind.annotation.adapte
import org.opengis.util.GenericName;
import org.opengis.metadata.citation.Citation;
import org.opengis.metadata.content.FeatureCatalogueDescription;
-import org.opengis.metadata.content.FeatureTypeInfo;
import org.apache.sis.internal.jaxb.FilterByVersion;
- import org.apache.sis.internal.jaxb.LegacyNamespaces;
+ import org.apache.sis.internal.xml.LegacyNamespaces;
import org.apache.sis.internal.jaxb.lan.LocaleAdapter;
import org.apache.sis.internal.metadata.Dependencies;
import org.apache.sis.internal.metadata.LegacyPropertyAdapter;
diff --cc core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/distribution/DefaultMedium.java
index 26e29d3,d8d92df..61c8f8a
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/distribution/DefaultMedium.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/distribution/DefaultMedium.java
@@@ -357,11 -371,11 +355,11 @@@ public class DefaultMedium extends ISOM
*
* @since 0.5
*/
- @Override
@XmlElement(name = "identifier")
@XmlJavaTypeAdapter(MD_Identifier.Since2014.class)
+ @UML(identifier="identifier", obligation=OPTIONAL, specification=ISO_19115)
public Identifier getIdentifier() {
- return NonMarshalledAuthority.getMarshallable(identifiers);
+ return super.getIdentifier();
}
/**
diff --cc core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/identification/DefaultAggregateInformation.java
index d3dee84,9d38770..b7525b1
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/identification/DefaultAggregateInformation.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/identification/DefaultAggregateInformation.java
@@@ -71,8 -66,10 +71,7 @@@ import org.apache.sis.internal.jaxb.cod
* @version 1.0
* @since 0.3
* @module
- *
- * @deprecated As of ISO 19115:2014, replaced by {@link DefaultAssociatedResource}.
*/
- @SuppressWarnings("CloneableClassWithoutClone") // ModifiableMetadata needs shallow clones.
-@Deprecated
@XmlType(name = "MD_AggregateInformation_Type", namespace = LegacyNamespaces.GMD, propOrder = {
"aggregateDataSetName",
"aggregateDataSetIdentifier",
diff --cc core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/lineage/DefaultSource.java
index 711f7e0,99fe75b..33e8949
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/lineage/DefaultSource.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/lineage/DefaultSource.java
@@@ -437,13 -425,16 +436,16 @@@ public class DefaultSource extends ISOM
public Collection<Extent> getSourceExtents() {
if (FilterByVersion.LEGACY_METADATA.accept()) {
Scope scope = getScope();
- if (!(scope instanceof DefaultScope)) {
- if (isModifiable()) {
- scope = new DefaultScope(scope);
- this.scope = scope;
- } else {
- return Collections.singleton(scope.getExtent());
+ if (scope != null) {
+ if (!(scope instanceof DefaultScope)) {
+ if (super.state() != State.FINAL) {
+ scope = new DefaultScope(scope);
+ this.scope = scope;
+ } else {
- return Collections.unmodifiableCollection(scope.getExtents());
++ return Collections.singleton(scope.getExtent());
+ }
}
+ return ((DefaultScope) scope).getExtents();
}
}
return null;
diff --cc core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/maintenance/DefaultMaintenanceInformation.java
index 420e962,c8f75ed..45116f9
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/maintenance/DefaultMaintenanceInformation.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/maintenance/DefaultMaintenanceInformation.java
@@@ -38,13 -38,8 +38,13 @@@ import org.apache.sis.metadata.iso.cita
import org.apache.sis.internal.metadata.LegacyPropertyAdapter;
import org.apache.sis.internal.metadata.Dependencies;
import org.apache.sis.internal.jaxb.FilterByVersion;
- import org.apache.sis.internal.jaxb.LegacyNamespaces;
+ import org.apache.sis.internal.xml.LegacyNamespaces;
+// Branch-specific imports
+import org.opengis.annotation.UML;
+import static org.opengis.annotation.Obligation.OPTIONAL;
+import static org.opengis.annotation.Specification.ISO_19115;
+
/**
* Information about the scope and frequency of updating.
diff --cc core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultDataQuality.java
index 1aa9d92,862495b..895be98
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultDataQuality.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultDataQuality.java
@@@ -26,9 -26,8 +26,9 @@@ import org.opengis.metadata.quality.Ele
import org.opengis.metadata.quality.Scope;
import org.opengis.metadata.maintenance.ScopeCode;
import org.apache.sis.metadata.iso.ISOMetadata;
+import org.apache.sis.metadata.iso.maintenance.DefaultScope;
import org.apache.sis.internal.jaxb.FilterByVersion;
- import org.apache.sis.internal.jaxb.LegacyNamespaces;
+ import org.apache.sis.internal.xml.LegacyNamespaces;
/**
diff --cc core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataSource.java
index a5dca40,e268e80..009ac61
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataSource.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataSource.java
@@@ -46,6 -46,8 +46,7 @@@ import java.sql.SQLNonTransientExceptio
import java.sql.PreparedStatement;
import org.opengis.annotation.UML;
import org.opengis.util.CodeList;
-import org.opengis.util.ControlledVocabulary;
+ import org.opengis.util.FactoryException;
import org.apache.sis.metadata.MetadataStandard;
import org.apache.sis.metadata.KeyNamePolicy;
import org.apache.sis.metadata.ValueExistencePolicy;
@@@ -929,8 -901,8 +903,8 @@@ public class MetadataSource implements
* If the identifier is prefixed with a table name as in "{CI_Organisation}identifier",
* the name between bracket is a subtype of the given 'type' argument.
*/
- final Class<?> type = subType(info.getMetadataType(), toSearch.identifier);
+ final Class<?> type = TableHierarchy.subType(info.getMetadataType(), toSearch.identifier);
- final Class<?> returnType = method.getReturnType();
+ final Class<?> returnType = Interim.getReturnType(method);
final boolean wantCollection = Collection.class.isAssignableFrom(returnType);
final Class<?> elementType = wantCollection ? Classes.boundOfParameterizedProperty(method) : returnType;
final boolean isMetadata = standard.isMetadata(elementType);
diff --cc core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataWriter.java
index 6685e34,f205d0f..3f672c2
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataWriter.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataWriter.java
@@@ -31,9 -33,9 +33,10 @@@ import java.sql.SQLException
import javax.sql.DataSource;
import java.lang.reflect.Modifier;
+import org.opengis.util.CodeList;
import org.opengis.metadata.Identifier;
import org.opengis.metadata.citation.Citation;
+ import org.opengis.util.FactoryException;
import org.apache.sis.util.Exceptions;
import org.apache.sis.util.ArgumentChecks;
@@@ -49,9 -51,10 +52,10 @@@ import org.apache.sis.metadata.ValueExi
import org.apache.sis.metadata.TitleProperty;
import org.apache.sis.metadata.iso.citation.Citations;
import org.apache.sis.internal.metadata.sql.SQLBuilder;
+ import org.apache.sis.xml.IdentifiedObject;
// Branch-dependent imports
-import org.opengis.util.ControlledVocabulary;
+import org.opengis.referencing.ReferenceIdentifier;
/**
@@@ -641,9 -646,11 +647,9 @@@ public class MetadataWriter extends Met
/**
* Adds a code list if it is not already present. This is used only in order to enforce
* foreigner key constraints in the database. The value of CodeList tables are not used
- * at parsing time. Enumerations are handled as if they were CodeLists; we do not use
- * the native SQL {@code ENUM} type for making easier to add new values when a standard
- * is updated.
+ * at parsing time.
*/
- private String addCode(final Statement stmt, final CodeList<?> code) throws SQLException {
- private String addCode(final Statement stmt, final ControlledVocabulary code) throws SQLException, FactoryException {
++ private String addCode(final Statement stmt, final CodeList<?> code) throws SQLException, FactoryException {
assert Thread.holdsLock(this);
final String table = getTableName(code.getClass());
final Set<String> columns = getExistingColumns(table);
@@@ -687,13 -694,22 +693,24 @@@
*/
protected String suggestIdentifier(final Object metadata, final Map<String,Object> asValueMap) throws SQLException {
String identifier = null;
+ final Collection<? extends Identifier> identifiers;
if (metadata instanceof Identifier) {
- identifier = nonEmpty(((Identifier) metadata).getCode());
- if (metadata instanceof ReferenceIdentifier) {
- final String cs = nonEmpty(((ReferenceIdentifier) metadata).getCodeSpace());
- if (cs != null) {
- identifier = (identifier != null) ? (cs + DefaultNameSpace.DEFAULT_SEPARATOR + identifier) : cs;
+ identifiers = Collections.singleton((Identifier) metadata);
+ } else if (metadata instanceof IdentifiedObject) {
+ identifiers = ((IdentifiedObject) metadata).getIdentifiers();
+ } else {
+ identifiers = Collections.emptySet();
+ }
+ for (final Identifier id : identifiers) {
+ identifier = nonEmpty(id.getCode());
+ if (identifier != null) {
- final String cs = nonEmpty(id.getCodeSpace());
- if (cs != null) {
- identifier = cs + DefaultNameSpace.DEFAULT_SEPARATOR + identifier;
++ if (id instanceof ReferenceIdentifier) {
++ final String cs = nonEmpty(((ReferenceIdentifier) id).getCodeSpace());
++ if (cs != null) {
++ identifier = cs + DefaultNameSpace.DEFAULT_SEPARATOR + identifier;
++ }
}
+ break;
}
}
if (identifier == null && metadata instanceof Citation) {
diff --cc core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultRecordSchema.java
index 07cfd5d,694008c..201b55d
--- a/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultRecordSchema.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/util/iso/DefaultRecordSchema.java
@@@ -29,7 -29,7 +29,6 @@@ import org.opengis.util.NameFactory
import org.opengis.util.NameSpace;
import org.opengis.util.RecordSchema;
import org.opengis.util.RecordType;
- import org.apache.sis.util.Debug;
-import org.opengis.feature.AttributeType;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.ObjectConverter;
import org.apache.sis.util.ObjectConverters;
diff --cc core/sis-metadata/src/main/java/org/apache/sis/util/iso/RecordDefinition.java
index eb0183b,2efa1f8..0fe49b3
--- a/core/sis-metadata/src/main/java/org/apache/sis/util/iso/RecordDefinition.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/util/iso/RecordDefinition.java
@@@ -26,7 -26,7 +26,6 @@@ import javax.xml.bind.annotation.XmlTra
import org.opengis.util.Type;
import org.opengis.util.RecordType;
import org.opengis.util.MemberName;
- import org.apache.sis.util.Debug;
-import org.opengis.feature.AttributeType;
import org.apache.sis.util.Classes;
import org.apache.sis.util.Numbers;
import org.apache.sis.util.CharSequences;
diff --cc core/sis-metadata/src/test/java/org/apache/sis/internal/jaxb/cat/CodeListMarshallingTest.java
index fb3ec87,e0e91b5..115249a
--- a/core/sis-metadata/src/test/java/org/apache/sis/internal/jaxb/cat/CodeListMarshallingTest.java
+++ b/core/sis-metadata/src/test/java/org/apache/sis/internal/jaxb/cat/CodeListMarshallingTest.java
@@@ -24,11 -24,11 +24,11 @@@ import javax.xml.bind.JAXBException
import org.opengis.metadata.citation.Role;
import org.opengis.metadata.citation.DateType;
import org.opengis.metadata.citation.CitationDate;
-import org.opengis.metadata.citation.Responsibility;
+import org.opengis.metadata.citation.ResponsibleParty;
import org.opengis.metadata.citation.PresentationForm;
import org.apache.sis.metadata.iso.citation.DefaultCitation;
- import org.apache.sis.internal.jaxb.LegacyNamespaces;
- import org.apache.sis.internal.jaxb.Schemas;
+ import org.apache.sis.internal.xml.LegacyNamespaces;
+ import org.apache.sis.internal.xml.Schemas;
import org.apache.sis.xml.XML;
import org.apache.sis.xml.Namespaces;
import org.apache.sis.xml.MarshallerPool;
diff --cc core/sis-metadata/src/test/java/org/apache/sis/metadata/HashCodeTest.java
index 0000000,6e49ec9..a85ef32
mode 000000,100644..100644
--- a/core/sis-metadata/src/test/java/org/apache/sis/metadata/HashCodeTest.java
+++ b/core/sis-metadata/src/test/java/org/apache/sis/metadata/HashCodeTest.java
@@@ -1,0 -1,155 +1,154 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.metadata;
+
+ import java.util.Arrays;
+ import org.opengis.util.InternationalString;
+ import org.opengis.metadata.Identifier;
+ import org.opengis.metadata.citation.Role;
+ import org.opengis.metadata.citation.Citation;
-import org.opengis.metadata.citation.Individual;
+ import org.opengis.metadata.citation.ResponsibleParty;
+ import org.opengis.metadata.acquisition.Instrument;
+ import org.opengis.metadata.acquisition.Platform;
+ import org.apache.sis.util.iso.SimpleInternationalString;
+ import org.apache.sis.metadata.iso.DefaultIdentifier;
+ import org.apache.sis.metadata.iso.citation.DefaultCitation;
+ import org.apache.sis.metadata.iso.citation.DefaultIndividual;
+ import org.apache.sis.metadata.iso.citation.DefaultResponsibleParty;
+ import org.apache.sis.metadata.iso.acquisition.DefaultInstrument;
+ import org.apache.sis.metadata.iso.acquisition.DefaultPlatform;
+ import org.apache.sis.test.DependsOnMethod;
+ import org.apache.sis.test.DependsOn;
+ import org.apache.sis.test.TestCase;
+ import org.junit.Test;
+
+ import static java.util.Collections.singleton;
+ import static org.junit.Assert.*;
+
+
+ /**
+ * Tests the {@link HashCode} class. This is also used as a relatively simple {@link MetadataVisitor} test.
+ * The entry point is the {@link HashCode#walk(MetadataStandard, Class, Object, boolean)} method.
+ *
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.0
+ * @since 1.0
+ * @module
+ */
+ @DependsOn(PropertyAccessorTest.class)
+ public final strictfp class HashCodeTest extends TestCase {
+ /**
+ * Computes the hash code value of the given object.
+ */
+ private static Integer hash(final Object metadata) {
+ return HashCode.getOrCreate().walk(MetadataStandard.ISO_19115, null, metadata, true);
+ }
+
+ /**
+ * Tests hash code computation of an object that do not contain other metadata.
+ */
+ @Test
+ public void testSimple() {
+ final DefaultCitation instance = new DefaultCitation();
+ final int baseCode = Citation.class.hashCode();
+ assertEquals("Empty metadata.", Integer.valueOf(baseCode), hash(instance));
+
+ final InternationalString title = new SimpleInternationalString("Some title");
+ instance.setTitle(title);
+ assertEquals("Metadata with a single value.", Integer.valueOf(baseCode + title.hashCode()), hash(instance));
+
+ final InternationalString alternateTitle = new SimpleInternationalString("An other title");
+ instance.setAlternateTitles(singleton(alternateTitle));
+ assertEquals("Metadata with two values.",
+ Integer.valueOf(baseCode + title.hashCode() + Arrays.asList(alternateTitle).hashCode()),
+ hash(instance));
+ }
+
+ /**
+ * Tests hash code computation of an object containing another metadata object.
+ */
+ @Test
+ @DependsOnMethod("testSimple")
+ public void testNested() {
+ final InternationalString title = new SimpleInternationalString("Some title");
+ final InternationalString person = new SimpleInternationalString("Illustre inconnu");
+ final DefaultIndividual party = new DefaultIndividual(person, null, null);
+ final DefaultResponsibleParty resp = new DefaultResponsibleParty(Role.AUTHOR);
+ final DefaultCitation instance = new DefaultCitation(title);
+ resp.getParties().add(party);
+ instance.getCitedResponsibleParties().add(resp);
+ /*
+ * Individual hash code is the sum of all its properties, none of them being a collection.
+ */
- int expected = Individual.class.hashCode() + person.hashCode();
++ int expected = DefaultIndividual.class.hashCode() + person.hashCode();
+ assertEquals("Individual", Integer.valueOf(expected), hash(party));
+ /*
+ * The +31 below come from java.util.List contract, since above Individual is a list member.
+ */
+ expected += ResponsibleParty.class.hashCode() + Role.AUTHOR.hashCode() + 31;
+ assertEquals("Responsibility", Integer.valueOf(expected), hash(resp));
+ /*
+ * The +31 below come from java.util.List contract, since above Responsibility is a list member.
+ */
+ expected += Citation.class.hashCode() + title.hashCode() + 31;
+ assertEquals("Citation", Integer.valueOf(expected), hash(instance));
+ }
+
+ /**
+ * Tests hash code computation of an object graph containing a cycle.
+ */
+ @Test
+ @DependsOnMethod("testNested")
+ public void testCycle() {
+ /*
+ * We will create a Platform and an Instrument, both of them with no other property than an identifier.
+ * The assertions verifying Identifier hash codes are not the main purpose of this test, but we perform
+ * those verifications for making sure that the assertion done at the end of this method has good premises.
+ */
+ final DefaultIdentifier platformID = new DefaultIdentifier("P1");
+ final DefaultIdentifier instrumentID = new DefaultIdentifier("I1");
+ int platformHash = Identifier.class.hashCode() + platformID.getCode().hashCode();
+ int instrumentHash = Identifier.class.hashCode() + instrumentID.getCode().hashCode();
+ assertEquals("platformID", Integer.valueOf(platformHash), hash(platformID));
+ assertEquals("instrumentID", Integer.valueOf(instrumentHash), hash(instrumentID));
+ /*
+ * Verify Platform and Instrument hash codes before we link them together.
+ */
+ final DefaultPlatform platform = new DefaultPlatform();
+ final DefaultInstrument instrument = new DefaultInstrument();
+ platform .setIdentifier(platformID);
+ instrument.setIdentifier(instrumentID);
+ platformHash += Platform.class.hashCode();
+ instrumentHash += Instrument.class.hashCode();
+ assertEquals("Platform", Integer.valueOf(platformHash), hash(platform));
+ assertEquals("Instrument", Integer.valueOf(instrumentHash), hash(instrument));
+ /*
+ * Add the instrument to the platform. The +31 below come from java.util.List contract,
+ * since the Instrument is contained in a list.
+ */
+ platform.getInstruments().add(instrument);
+ platformHash += instrumentHash + 31;
+ assertEquals("Platform", Integer.valueOf(platformHash), hash(platform));
+ /*
+ * Add a reference from the instrument back to the platform. This is where the graph become cyclic.
+ * The hash code computation is expected to behave as if the platform was not specified.
+ */
+ instrument.setMountedOn(platform);
+ assertEquals("Platform", Integer.valueOf(platformHash), hash(platform));
+ }
+ }
diff --cc core/sis-metadata/src/test/java/org/apache/sis/test/MetadataAssert.java
index a52d205,985c525..664cdeb
--- a/core/sis-metadata/src/test/java/org/apache/sis/test/MetadataAssert.java
+++ b/core/sis-metadata/src/test/java/org/apache/sis/test/MetadataAssert.java
@@@ -27,11 -27,10 +27,11 @@@ import org.apache.sis.io.wkt.Symbols
import org.apache.sis.io.wkt.WKTFormat;
import org.apache.sis.io.wkt.Convention;
import org.apache.sis.xml.Namespaces;
- import org.apache.sis.internal.jaxb.LegacyNamespaces;
+ import org.apache.sis.internal.xml.LegacyNamespaces;
// Branch-specific imports
-import org.opengis.metadata.citation.Responsibility;
+import org.apache.sis.metadata.iso.citation.DefaultCitation;
+import org.apache.sis.metadata.iso.citation.DefaultResponsibility;
/**
diff --cc core/sis-metadata/src/test/java/org/apache/sis/test/xml/AnnotationConsistencyCheck.java
index 14a709f,04fd9c5..c4827b2
--- a/core/sis-metadata/src/test/java/org/apache/sis/test/xml/AnnotationConsistencyCheck.java
+++ b/core/sis-metadata/src/test/java/org/apache/sis/test/xml/AnnotationConsistencyCheck.java
@@@ -34,10 -34,13 +34,10 @@@ import org.opengis.annotation.UML
import org.opengis.annotation.Obligation;
import org.opengis.annotation.Specification;
import org.opengis.util.CodeList;
-import org.opengis.util.ControlledVocabulary;
import org.apache.sis.util.ArraysExt;
import org.apache.sis.xml.Namespaces;
- import org.apache.sis.internal.jaxb.Schemas;
- import org.apache.sis.internal.jaxb.LegacyNamespaces;
+ import org.apache.sis.internal.xml.Schemas;
+ import org.apache.sis.internal.xml.LegacyNamespaces;
import org.apache.sis.test.DependsOnMethod;
import org.apache.sis.test.TestUtilities;
import org.apache.sis.test.TestCase;
@@@ -65,7 -68,7 +65,6 @@@ import junit.framework.AssertionFailedE
* </ul>
*
* This class does not verify JAXB annotations against a XSD file.
-- * For such verification, see {@link SchemaCompliance}.
*
* @author Cédric Briançon (Geomatys)
* @author Martin Desruisseaux (Geomatys)
@@@ -218,7 -222,7 +217,7 @@@ public abstract strictfp class Annotati
* with a hard-coded list of exceptions to the general rule.
* Subclasses shall override this method if they need to support more namespaces.</p>
*
-- * <p>Note that a more complete verification is done by {@link SchemaCompliance}.
++ * <p>Note that a more complete verification is done by {@code SchemaCompliance}.
* But the test done in this {@link AnnotationConsistencyCheck} class can be run without network access.</p>
*
* <p>The prefix for the given namespace will be fetched by
diff --cc core/sis-metadata/src/test/java/org/apache/sis/test/xml/PackageVerifier.java
index bf52a98,a964214..0000000
deleted file mode 100644,100644
--- a/core/sis-metadata/src/test/java/org/apache/sis/test/xml/PackageVerifier.java
+++ /dev/null
@@@ -1,513 -1,514 +1,0 @@@
--/*
-- * Licensed to the Apache Software Foundation (ASF) under one or more
-- * contributor license agreements. See the NOTICE file distributed with
-- * this work for additional information regarding copyright ownership.
-- * The ASF licenses this file to You under the Apache License, Version 2.0
-- * (the "License"); you may not use this file except in compliance with
-- * the License. You may obtain a copy of the License at
-- *
-- * http://www.apache.org/licenses/LICENSE-2.0
-- *
-- * Unless required by applicable law or agreed to in writing, software
-- * distributed under the License is distributed on an "AS IS" BASIS,
-- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- * See the License for the specific language governing permissions and
-- * limitations under the License.
-- */
--package org.apache.sis.test.xml;
--
--import java.util.Map;
--import java.util.Set;
--import java.util.HashMap;
--import java.util.Collection;
--import java.util.Collections;
--import java.io.IOException;
--import java.lang.reflect.Type;
--import java.lang.reflect.Field;
--import java.lang.reflect.Method;
--import java.lang.reflect.AnnotatedElement;
--import java.lang.reflect.ParameterizedType;
--import javax.xml.bind.annotation.XmlNs;
--import javax.xml.bind.annotation.XmlType;
--import javax.xml.bind.annotation.XmlSchema;
--import javax.xml.bind.annotation.XmlElement;
--import javax.xml.bind.annotation.XmlRootElement;
--import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
--import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;
--import javax.xml.parsers.ParserConfigurationException;
--import org.xml.sax.SAXException;
--import org.opengis.annotation.UML;
-import org.opengis.geoapi.SchemaException;
--import org.apache.sis.util.Classes;
--import org.apache.sis.internal.system.Modules;
- import org.apache.sis.internal.jaxb.LegacyNamespaces;
-import org.apache.sis.internal.xml.LegacyNamespaces;
--import org.apache.sis.xml.Namespaces;
--
--
--/**
-- * Verify JAXB annotations in a single package. A new instance of this class is created by
-- * {@link SchemaCompliance#verify(java.nio.file.Path)} for each Java package to be verified.
-- *
-- * @author Martin Desruisseaux (Geomatys)
-- * @version 1.0
-- * @since 1.0
-- * @module
-- */
--final strictfp class PackageVerifier {
-- /**
-- * Sentinel value used in {@link #LEGACY_NAMESPACES} for meaning "all properties in that namespace".
-- */
-- @SuppressWarnings("unchecked")
-- private static final Set<String> ALL = InfiniteSet.INSTANCE;
--
-- /**
-- * Classes or properties having a JAXB annotation in this namespace should be deprecated.
-- * Deprecated namespaces are enumerated as keys. If the associated value is {@link #ALL},
-- * the whole namespace is deprecated. If the value is not ALL, then only the enumerated
-- * properties are deprecated.
-- *
-- * <p>Non-ALL values are rare. They happen in a few cases where a property is legacy despite its namespace.
-- * Those "properties" are errors in the legacy ISO 19139:2007 schema; they were types without their property
-- * wrappers. For example in {@code SV_CoupledResource}, {@code <gco:ScopedName>} was marshalled without its
-- * {@code <srv:scopedName>} wrapper — note the upper and lower-case "s". Because {@code ScopedName} is a type,
-- * we had to keep the namespace declared in {@link org.apache.sis.util.iso.DefaultScopedName}
-- * (the replacement is performed by {@code org.apache.sis.xml.TransformingWriter}).</p>
-- */
-- private static final Map<String, Set<String>> LEGACY_NAMESPACES;
-- static {
-- final Map<String, Set<String>> m = new HashMap<>(8);
-- m.put(LegacyNamespaces.GMD, ALL);
-- m.put(LegacyNamespaces.GMI, ALL);
-- m.put(LegacyNamespaces.GMX, ALL);
-- m.put(LegacyNamespaces.SRV, ALL);
-- m.put(Namespaces.GCO, Collections.singleton("ScopedName")); // Not to be confused with standard <srv:scopedName>
-- LEGACY_NAMESPACES = Collections.unmodifiableMap(m);
-- }
--
-- /**
-- * Types declared in JAXB annotations to be considered as equivalent to types in XML schemas.
-- */
-- private static final Map<String,String> TYPE_EQUIVALENCES;
-- static {
-- final Map<String,String> m = new HashMap<>();
-- m.put("PT_FreeText", "CharacterString");
-- m.put("Abstract_Citation", "CI_Citation");
-- m.put("AbstractCI_Party", "CI_Party");
-- m.put("Abstract_Responsibility", "CI_Responsibility");
-- m.put("Abstract_Extent", "EX_Extent");
-- TYPE_EQUIVALENCES = Collections.unmodifiableMap(m);
-- }
--
-- /**
-- * The schemas to compare with the JAXB annotations.
-- * Additional schemas will be loaded as needed.
-- */
-- private final SchemaCompliance schemas;
--
-- /**
-- * The package name, for reporting error.
-- */
-- private final String packageName;
--
-- /**
-- * The default namespace to use if a class does not define explicitely a namespace.
-- */
-- private final String packageNS;
--
-- /**
-- * The namespace of the class under examination.
-- * This field must be updated for every class found in a package.
-- */
-- private String classNS;
--
-- /**
-- * The class under examination, used in error messages.
-- * This field must be updated for every class found in a package.
-- */
-- private Class<?> currentClass;
--
-- /**
-- * Whether the class under examination is defined in a legacy namespace.
-- * In such case, some checks may be skipped because we didn't loaded schemas for legacy properties.
-- */
-- private boolean isDeprecatedClass;
--
-- /**
-- * The schema definition for the class under examination.
-- *
- * @see SchemaCompliance#typeDefinition(String)
- * @see SchemaCompliance#getTypeDefinition(String)
-- */
- private Map<String, SchemaCompliance.Info> properties;
- private Map<String, SchemaCompliance.Element> properties;
--
-- /**
-- * Whether a namespace is actually used of not.
-- * We use this map for identifying unnecessary prefix declarations.
-- */
-- private final Map<String,Boolean> namespaceIsUsed;
--
-- /**
-- * Whether adapters declared in {@code package-info.java} are used or not.
-- */
-- private final Map<Class<?>,Boolean> adapterIsUsed;
--
-- /**
-- * Creates a new verifier for the given package.
-- */
-- PackageVerifier(final SchemaCompliance schemas, final Package pkg)
-- throws IOException, ParserConfigurationException, SAXException, SchemaException
-- {
-- this.schemas = schemas;
-- namespaceIsUsed = new HashMap<>();
-- adapterIsUsed = new HashMap<>();
-- String name = "?", namespace = "";
-- if (pkg != null) {
-- name = pkg.getName();
-- final XmlSchema schema = pkg.getAnnotation(XmlSchema.class);
-- if (schema != null) {
-- namespace = schema.namespace();
-- String location = schema.location();
-- if (!XmlSchema.NO_LOCATION.equals(location)) {
-- if (!location.startsWith(schema.namespace())) {
-- throw new SchemaException("XML schema location inconsistent with namespace in package " + name);
-- }
-- schemas.loadSchema(location);
-- }
-- for (final XmlNs xmlns : schema.xmlns()) {
-- final String pr = xmlns.prefix();
-- final String ns = xmlns.namespaceURI();
-- final String cr = schemas.allXmlNS.put(pr, ns);
-- if (cr != null && !cr.equals(ns)) {
-- throw new SchemaException(String.format("Prefix \"%s\" associated to two different namespaces:%n%s%n%s", pr, cr, ns));
-- }
-- if (namespaceIsUsed.put(ns, Boolean.FALSE) != null) {
-- throw new SchemaException(String.format("Duplicated namespace in package %s:%n%s", name, ns));
-- }
-- }
-- }
-- /*
-- * Lists the type of all values for which an adapter is declared in package-info.
-- * If the type is not explicitely declared, then it is inferred from class signature.
-- */
-- final XmlJavaTypeAdapters adapters = pkg.getAnnotation(XmlJavaTypeAdapters.class);
-- if (adapters != null) {
-- for (final XmlJavaTypeAdapter adapter : adapters.value()) {
-- Class<?> propertyType = adapter.type();
-- if (propertyType == XmlJavaTypeAdapter.DEFAULT.class) {
-- for (Class<?> c = adapter.value(); ; c = c.getSuperclass()) {
-- final Type type = c.getGenericSuperclass();
-- if (type == null) {
-- throw new SchemaException(String.format(
-- "Can not infer type for %s adapter.", adapter.value().getName()));
-- }
-- if (type instanceof ParameterizedType) {
-- final Type[] p = ((ParameterizedType) type).getActualTypeArguments();
-- if (p.length == 2) {
-- Type pt = p[1];
-- if (pt instanceof ParameterizedType) {
-- pt = ((ParameterizedType) pt).getRawType();
-- }
-- if (pt instanceof Class<?>) {
-- propertyType = (Class<?>) pt;
-- break;
-- }
-- }
-- }
-- }
-- }
-- if (adapterIsUsed.put((Class<?>) propertyType, Boolean.FALSE) != null) {
-- throw new SchemaException(String.format(
-- "More than one adapter for %s in package %s", propertyType, name));
-- }
-- }
-- }
-- }
-- packageName = name;
-- packageNS = namespace;
-- }
--
-- /**
-- * Verifies {@code @XmlType} and {@code @XmlRootElement} on the class. This method verifies naming convention
-- * (type name should be same as root element name with {@value SchemaCompliance#TYPE_SUFFIX} suffix appended),
-- * ensures that the name exists in the schema, and checks the namespace.
-- *
-- * @param type the class on which to verify annotations.
-- */
-- final void verify(final Class<?> type)
-- throws IOException, ParserConfigurationException, SAXException, SchemaException
-- {
-- /*
-- * Reinitialize fields to be updated for each class.
-- */
-- classNS = null;
-- currentClass = type;
-- isDeprecatedClass = false;
-- properties = Collections.emptyMap();
--
-- final XmlType xmlType = type.getDeclaredAnnotation(XmlType.class);
-- final XmlRootElement xmlRoot = type.getDeclaredAnnotation(XmlRootElement.class);
-- XmlElement codeList = null;
-- /*
-- * Get the type name and namespace from the @XmlType or @XmlRootElement annotations.
-- * If both of them are present, verify that they are consistent (same namespace and
-- * same name with "_Type" suffix in @XmlType). If the type name is not declared, we
-- * assume that it is the same than the class name (this is what Apache SIS 0.8 does
-- * in its org.apache.sis.internal.jaxb.code package for CodeList adapters).
-- */
-- final String isoName; // ISO class name (not the same than Java class name).
-- if (xmlRoot != null) {
-- classNS = xmlRoot.namespace();
-- isoName = xmlRoot.name();
-- if (xmlType != null) {
-- if (!classNS.equals(xmlType.namespace())) {
-- throw new SchemaException(errorInClassMember(null)
- .append("Mismatched namespace in @XmlType and @XmlRootElement."));
- .append("Mismatched namespace in @XmlType and @XmlRootElement.").toString());
-- }
-- SchemaCompliance.verifyNamingConvention(type.getName(), isoName, xmlType.name(), SchemaCompliance.TYPE_SUFFIX);
-- }
-- } else if (xmlType != null) {
-- classNS = xmlType.namespace();
-- final String name = xmlType.name();
-- isoName = SchemaCompliance.trim(name, SchemaCompliance.TYPE_SUFFIX);
-- } else {
-- /*
-- * If there is neither @XmlRootElement or @XmlType annotation, it may be a code list as implemented
-- * in the org.apache.sis.internal.jaxb.code package. Those adapters have a single @XmlElement which
-- * is to be interpreted as if it was the actual type.
-- */
-- for (final Method method : type.getDeclaredMethods()) {
-- final XmlElement e = method.getDeclaredAnnotation(XmlElement.class);
-- if (e != null) {
-- if (codeList != null) return;
-- codeList = e;
-- }
-- }
-- if (codeList == null) return;
-- classNS = codeList.namespace();
-- isoName = codeList.name();
-- }
-- /*
-- * Verify that the namespace declared on the class is not redundant with the namespace
-- * declared in the package. Actually redundant namespaces are not wrong, but we try to
-- * reduce code size.
-- */
-- if (classNS.equals(AnnotationConsistencyCheck.DEFAULT)) {
-- classNS = packageNS;
-- } else if (classNS.equals(packageNS)) {
-- throw new SchemaException(errorInClassMember(null)
- .append("Redundant namespace declaration: ").append(classNS));
- .append("Redundant namespace declaration: ").append(classNS).toString());
-- }
-- /*
-- * Verify that the namespace has a prefix associated to it in the package-info file.
-- */
-- if (namespaceIsUsed.put(classNS, Boolean.TRUE) == null) {
-- throw new SchemaException(errorInClassMember(null)
- .append("No prefix in package-info for ").append(classNS));
- .append("No prefix in package-info for ").append(classNS).toString());
-- }
-- /*
-- * Properties in the legacy GMD or GMI namespaces may be deprecated, depending if a replacement
-- * is already available or not. However properties in other namespaces should not be deprecated.
-- * Some validations of deprecated properties are skipped because we didn't loaded their schema.
-- */
-- isDeprecatedClass = (LEGACY_NAMESPACES.get(classNS) == ALL);
-- if (!isDeprecatedClass) {
-- if (type.isAnnotationPresent(Deprecated.class)) {
-- throw new SchemaException(errorInClassMember(null)
- .append("Unexpected @Deprecated annotation."));
- .append("Unexpected @Deprecated annotation.").toString());
-- }
-- /*
-- * Verify that class name exists, then verify its namespace (associated to the null key by convention).
-- */
- properties = schemas.typeDefinition(isoName);
- properties = schemas.getTypeDefinition(isoName);
-- if (properties == null) {
-- throw new SchemaException(errorInClassMember(null)
- .append("Unknown name declared in @XmlRootElement: ").append(isoName));
- .append("Unknown name declared in @XmlRootElement: ").append(isoName).toString());
-- }
-- final String expectedNS = properties.get(null).namespace;
-- if (!classNS.equals(expectedNS)) {
-- throw new SchemaException(errorInClassMember(null)
- .append(isoName).append(" shall be associated to namespace ").append(expectedNS));
- .append(isoName).append(" shall be associated to namespace ").append(expectedNS).toString());
-- }
-- if (codeList != null) return; // If the class was a code list, we are done.
-- }
-- /*
-- * At this point the classNS, className, isDeprecatedClass and properties field have been set.
-- * We can now loop over the XML elements, which may be on fields or on methods (public or private).
-- */
-- for (final Field field : type.getDeclaredFields()) {
-- Class<?> valueType = field.getType();
-- final boolean isCollection = Collection.class.isAssignableFrom(valueType);
-- if (isCollection) {
-- valueType = Classes.boundOfParameterizedProperty(field);
-- }
-- verify(field, field.getName(), valueType, isCollection);
-- }
-- for (final Method method : type.getDeclaredMethods()) {
-- Class<?> valueType = method.getReturnType();
-- final boolean isCollection = Collection.class.isAssignableFrom(valueType);
-- if (isCollection) {
-- valueType = Classes.boundOfParameterizedProperty(method);
-- }
-- verify(method, method.getName(), valueType, isCollection);
-- }
-- }
--
-- /**
-- * Validate a field or a method against the expected schema.
-- *
-- * @param property the field or method to validate.
-- * @param javaName the field name or method name in Java code.
-- * @param valueType the field type or the method return type, or element type in case of collection.
-- * @param isCollection whether the given value type is the element type of a collection.
-- */
-- private void verify(final AnnotatedElement property, final String javaName,
-- final Class<?> valueType, final boolean isCollection) throws SchemaException
-- {
-- final XmlElement element = property.getDeclaredAnnotation(XmlElement.class);
-- if (element == null) {
-- return; // No @XmlElement annotation - skip this property.
-- }
-- String name = element.name();
-- if (name.equals(AnnotationConsistencyCheck.DEFAULT)) {
-- name = javaName;
-- }
-- String ns = element.namespace();
-- if (ns.equals(AnnotationConsistencyCheck.DEFAULT)) {
-- ns = classNS;
-- }
-- if (namespaceIsUsed.put(ns, Boolean.TRUE) == null) {
-- throw new SchemaException(errorInClassMember(javaName)
- .append("Missing @XmlNs for namespace ").append(ns));
- .append("Missing @XmlNs for namespace ").append(ns).toString());
-- }
-- /*
-- * Remember that we need an adapter for this property, unless the method or field defines its own adapter.
-- * In theory we do not need to report missing adapter since JAXB performs its own check, but we do anyway
-- * because JAXB has default adapters for String, Double, Boolean, Date, etc. which do not match the way
-- * OGC/ISO marshal those elements.
-- */
-- if (!property.isAnnotationPresent(XmlJavaTypeAdapter.class) && (valueType != null)
-- && !valueType.getName().startsWith(Modules.CLASSNAME_PREFIX))
-- {
-- Class<?> c = valueType;
-- while (adapterIsUsed.replace(c, Boolean.TRUE) == null) {
-- final Class<?> parent = c.getSuperclass();
-- if (parent != null) {
-- c = parent;
-- } else {
-- final Class<?>[] p = c.getInterfaces();
-- if (p.length == 0) {
-- throw new SchemaException(errorInClassMember(javaName)
- .append("Missing @XmlJavaTypeAdapter for ").append(valueType));
- .append("Missing @XmlJavaTypeAdapter for ").append(valueType).toString());
-- }
-- c = p[0]; // Take only the first interface, which should be the "main" parent.
-- }
-- }
-- }
-- /*
-- * We do not verify fully the properties in legacy namespaces because we didn't loaded their schemas.
-- * However we verify at least that those properties are not declared as required.
-- */
-- if (LEGACY_NAMESPACES.getOrDefault(ns, Collections.emptySet()).contains(name)) {
-- if (!isDeprecatedClass && element.required()) {
-- throw new SchemaException(errorInClassMember(javaName)
- .append("Legacy property should not be required."));
- .append("Legacy property should not be required.").toString());
-- }
-- } else {
-- /*
-- * Property in non-legacy namespaces should not be deprecated. Verify also their namespace
-- * and whether the property is required or optional, and whether it should be a collection.
-- */
-- if (property.isAnnotationPresent(Deprecated.class)) {
-- throw new SchemaException(errorInClassMember(javaName)
- .append("Unexpected deprecation status."));
- .append("Unexpected deprecation status.").toString());
-- }
- final SchemaCompliance.Info info = properties.get(name);
- final SchemaCompliance.Element info = properties.get(name);
-- if (info == null) {
-- throw new SchemaException(errorInClassMember(javaName)
- .append("Unexpected XML element: ").append(name));
- .append("Unexpected XML element: ").append(name).toString());
-- }
-- if (info.namespace != null && !ns.equals(info.namespace)) {
-- throw new SchemaException(errorInClassMember(javaName)
-- .append("Declared namespace: ").append(ns).append(System.lineSeparator())
- .append("Expected namespace: ").append(info.namespace));
- .append("Expected namespace: ").append(info.namespace).toString());
-- }
-- if (element.required() != info.isRequired) {
-- throw new SchemaException(errorInClassMember(javaName)
- .append("Expected @XmlElement(required = ").append(info.isRequired).append(')'));
- .append("Expected @XmlElement(required = ").append(info.isRequired).append(')').toString());
-- }
-- /*
-- * Following is a continuation of our check for cardinality, but also the beginning of the check
-- * for return value type. The return type should be an interface with a UML annotation; we check
-- * that this annotation contains the name of the expected type.
-- */
-- if (isCollection) {
-- if (!info.isCollection) {
-- if (false) // Temporarily disabled because require GeoAPI modifications.
- throw new SchemaException(errorInClassMember(javaName).append("Value should be a singleton."));
- throw new SchemaException(errorInClassMember(javaName).append("Value should be a singleton.").toString());
-- }
-- } else if (info.isCollection) {
-- if (false) // Temporarily disabled because require GeoAPI modifications.
- throw new SchemaException(errorInClassMember(javaName).append("Value should be a collection."));
- throw new SchemaException(errorInClassMember(javaName).append("Value should be a collection.").toString());
-- }
-- if (valueType != null) {
-- final UML valueUML = valueType.getAnnotation(UML.class);
-- if (valueUML != null) {
-- String expected = info.typeName;
-- String actual = valueUML.identifier();
-- expected = TYPE_EQUIVALENCES.getOrDefault(expected, expected);
-- actual = TYPE_EQUIVALENCES.getOrDefault(actual, actual);
-- if (!expected.equals(actual)) {
-- if (false) // Temporarily disabled because require GeoAPI modifications.
-- throw new SchemaException(errorInClassMember(javaName)
-- .append("Declared value type: ").append(actual).append(System.lineSeparator())
- .append("Expected value type: ").append(expected));
- .append("Expected value type: ").append(expected).toString());
-- }
-- }
-- }
-- /*
-- * Verify if we have a @XmlNs for the type of the value. This is probably not required, but we
-- * do that as a safety. A common namespace added by this check is Metadata Common Classes (MCC).
-- */
- final Map<String, SchemaCompliance.Info> valueInfo = schemas.typeDefinition(info.typeName);
- final Map<String, SchemaCompliance.Element> valueInfo = schemas.getTypeDefinition(info.typeName);
-- if (valueInfo != null) {
-- final String valueNS = valueInfo.get(null).namespace;
-- if (namespaceIsUsed.put(valueNS, Boolean.TRUE) == null) {
-- throw new SchemaException(errorInClassMember(javaName)
- .append("Missing @XmlNs for property value namespace: ").append(valueNS));
- .append("Missing @XmlNs for property value namespace: ").append(valueNS).toString());
-- }
-- }
-- }
-- }
--
-- /**
-- * Returns a message beginning with "Error in …", to be completed by the caller.
-- * This is an helper method for exception messages.
-- *
-- * @param name the property name, or {@code null} if none.
-- */
-- private StringBuilder errorInClassMember(final String name) {
-- final StringBuilder builder = new StringBuilder(80).append("Error in ");
-- if (isDeprecatedClass) {
-- builder.append("legacy ");
-- }
-- builder.append(currentClass.getCanonicalName());
-- if (name != null) {
-- builder.append('.').append(name);
-- }
-- return builder.append(':').append(System.lineSeparator());
-- }
--
-- /**
-- * Verifies if there is any unused namespace or adapter in package-info file.
-- */
-- final void reportUnused() throws SchemaException {
-- for (final Map.Entry<String,Boolean> entry : namespaceIsUsed.entrySet()) {
-- if (!entry.getValue()) {
-- throw new SchemaException(String.format("Unused namespace in package %s:%n%s", packageName, entry.getKey()));
-- }
-- }
-- for (final Map.Entry<Class<?>,Boolean> entry : adapterIsUsed.entrySet()) {
-- if (!entry.getValue()) {
-- throw new SchemaException(String.format("Unused adapter in package %s for %s.", packageName, entry.getKey()));
-- }
-- }
-- }
--}
diff --cc core/sis-metadata/src/test/java/org/apache/sis/test/xml/SchemaCompliance.java
index 1280c3a,ad6edcb..0000000
deleted file mode 100644,100644
--- a/core/sis-metadata/src/test/java/org/apache/sis/test/xml/SchemaCompliance.java
+++ /dev/null
@@@ -1,566 -1,200 +1,0 @@@
--/*
-- * Licensed to the Apache Software Foundation (ASF) under one or more
-- * contributor license agreements. See the NOTICE file distributed with
-- * this work for additional information regarding copyright ownership.
-- * The ASF licenses this file to You under the Apache License, Version 2.0
-- * (the "License"); you may not use this file except in compliance with
-- * the License. You may obtain a copy of the License at
-- *
-- * http://www.apache.org/licenses/LICENSE-2.0
-- *
-- * Unless required by applicable law or agreed to in writing, software
-- * distributed under the License is distributed on an "AS IS" BASIS,
-- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- * See the License for the specific language governing permissions and
-- * limitations under the License.
-- */
--package org.apache.sis.test.xml;
--
--import java.io.IOException;
- import java.io.InputStream;
- import java.net.URL;
--import java.nio.file.Path;
--import java.nio.file.Files;
--import java.nio.file.DirectoryStream;
--import java.nio.file.DirectoryIteratorException;
--import java.util.Map;
- import java.util.Deque;
--import java.util.HashMap;
- import java.util.ArrayDeque;
- import java.util.Objects;
- import java.util.Collections;
- import javax.xml.XMLConstants;
--import javax.xml.bind.annotation.XmlNs;
--import javax.xml.bind.annotation.XmlElement;
- import javax.xml.parsers.DocumentBuilderFactory;
--import javax.xml.parsers.ParserConfigurationException;
- import org.w3c.dom.Node;
- import org.w3c.dom.Document;
- import org.w3c.dom.NamedNodeMap;
--import org.xml.sax.SAXException;
-import org.opengis.geoapi.Departures;
-import org.opengis.geoapi.DocumentationStyle;
-import org.opengis.geoapi.SchemaInformation;
-import org.opengis.geoapi.SchemaException;
--import org.apache.sis.util.StringBuilders;
--
--
--/**
-- * Compares JAXB annotations against the ISO 19115 schemas. This test requires a connection to
-- * <a href="http://standards.iso.org/iso/19115/-3/">http://standards.iso.org/iso/19115/-3/</a>.
-- * All classes in a given directory are scanned.
-- *
-- * <div class="section">Limitations</div>
-- * Current implementation ignores the XML prefix (e.g. {@code "cit:"} in {@code "cit:CI_Citation"}).
-- * We assume that there is no name collision, especially given that {@code "CI_"} prefix in front of
-- * most OGC/ISO class names have the effect of a namespace. If a collision nevertheless happen, then
-- * an exception will be thrown.
-- *
-- * <p>Current implementation assumes that XML element name, type name, property name and property type
-- * name follow some naming convention. For example type names are suffixed with {@code "_Type"} in OGC
-- * schemas, while property type names are suffixed with {@code "_PropertyType"}. This class throws an
-- * exception if a type does not follow the expected naming convention. This requirement makes
-- * implementation easier, by reducing the amount of {@link Map}s that we need to manage.</p>
-- *
-- * @author Martin Desruisseaux (Geomatys)
-- * @version 1.0
-- * @since 1.0
-- * @module
-- */
- public final strictfp class SchemaCompliance {
- /**
- * The root of ISO schemas. May be replaced by {@link #schemaRootDirectory} if a local copy
- * is available for faster tests.
- */
- private static final String SCHEMA_ROOT_DIRECTORY = "http://standards.iso.org/iso/";
-
- /**
- * ISO 19115-2 classes to merge with ISO 19115-1 classes. For example ISO 19115-2 defines {@code MI_Band}
- * as an extension of ISO 19115-1 {@code MD_Band}, but GeoAPI and Apache SIS merges those two types in a
- * single class for simplicity. Consequently when reading the schema, we rename some {@code MI_*} types
- * as {@code MD_*} in order to store properties together.
- */
- private static final Map<String,String> TYPES_TO_MERGE;
- static {
- final Map<String,String> m = new HashMap<>();
- // ………Merge what…………………………………………………………Into……………………………………………
- m.put("MI_Band_Type", "MD_Band_Type");
- m.put("MI_CoverageDescription_Type", "MD_CoverageDescription_Type");
- m.put("MI_Georectified_Type", "MD_Georectified_Type");
- m.put("MI_Georeferenceable_Type", "MD_Georeferenceable_Type");
- m.put("LE_Source_Type", "LI_Source_Type");
- m.put("LE_ProcessStep_Type", "LI_ProcessStep_Type");
- m.put("AbstractMX_File_Type", "MX_DataFile_Type");
- m.put("Abstract_DataQuality_Type", "DQ_DataQuality_Type");
- m.put("Abstract_QualityElement_Type", "AbstractDQ_Element_Type");
- TYPES_TO_MERGE = Collections.unmodifiableMap(m);
- }
-
-public final strictfp class SchemaCompliance extends SchemaInformation {
-- /**
-- * The prefix of XML type names for properties. In ISO/OGC schemas, this prefix does not appear
-- * in the definition of class types but may appear in the definition of property types.
-- */
-- private static final String ABSTRACT_PREFIX = "Abstract_";
--
-- /**
-- * The suffix of XML type names for classes.
-- * This is used by convention in OGC/ISO standards (but not necessarily in other XSD).
-- */
-- static final String TYPE_SUFFIX = "_Type";
-
- /**
- * The suffix of XML property type names in a given class.
- * This is used by convention in OGC/ISO standards (but not necessarily in other XSD).
- */
- private static final String PROPERTY_TYPE_SUFFIX = "_PropertyType";
-
- /**
- * XML type to ignore because of key collisions in {@link #typeDefinitions}.
- * Those collisions occur because code lists are defined as links to the same file,
- * with only different anchor positions.
- */
- private static final String CODELIST_TYPE = "gco:CodeListValue_Type";
--
-- /**
-- * Separator between XML prefix and the actual name.
-- */
-- private static final char PREFIX_SEPARATOR = ':';
-
- /**
- * If the computer contains a local copy of ISO schemas, path to that directory. Otherwise {@code null}.
- * If non-null, the {@code "http://standards.iso.org/iso/"} prefix in URL will be replaced by that path.
- * This field is usually {@code null}, but can be set to a non-null value for making tests faster.
- */
- private final Path schemaRootDirectory;
--
-- /**
-- * Root directory from which to search for classes.
-- */
-- private final Path classRootDirectory;
-
- /**
- * A temporary buffer for miscellaneous string operations.
- * Valid only in a local scope since the content may change at any time.
- */
- private final StringBuilder buffer;
-
- /**
- * The DOM factory used for reading XSD schemas.
- */
- private final DocumentBuilderFactory factory;
-
- /**
- * URL of schemas loaded, for avoiding loading the same schema many time.
- * The last element on the queue is the schema in process of being loaded,
- * used for resolving relative paths in {@code <xs:include>} elements.
- */
- private final Deque<String> schemaLocations;
-
- /**
- * The type and namespace of a property or class. Used in {@link #typeDefinitions} map.
- */
- static final class Info {
- final String typeName;
- final String namespace;
- final boolean isRequired;
- final boolean isCollection;
-
- Info(final String typeName, final String namespace, final boolean isRequired, final boolean isCollection) {
- this.typeName = typeName;
- this.namespace = namespace;
- this.isRequired = isRequired;
- this.isCollection = isCollection;
- }
-
- boolean equal(final Info other) {
- return Objects.equals(typeName, other.typeName)
- && Objects.equals(namespace, other.namespace)
- && isRequired == other.isRequired
- && isCollection == other.isCollection;
- }
-
- @Override public String toString() {
- return typeName;
- }
- }
-
- /**
- * Definitions of XML type for each class. In OGC/ISO schemas, those definitions have the {@value #TYPE_SUFFIX}
- * suffix in their name (which is omitted). The value is another map, where keys are property names and values
- * are their types, having the {@link #PROPERTY_TYPE_SUFFIX} suffix in their name (which is omitted).
- */
- private final Map<String, Map<String,Info>> typeDefinitions;
-
- /**
- * Notifies that we are about to define the XML type for each property. In OGC/ISO schemas, those definitions
- * have the {@value #PROPERTY_TYPE_SUFFIX} suffix in their name (which is omitted). After this method call,
- * properties can be defined by calls to {@link #addProperty(String, String, boolean, boolean)}.
- */
- private void preparePropertyDefinitions(final String type) throws SchemaException {
- currentProperties = typeDefinitions.computeIfAbsent(trim(type, TYPE_SUFFIX).intern(), (k) -> new HashMap<>());
- }
-
- /**
- * The properties of the XML type under examination, or {@code null} if none.
- * If non-null, this is one of the values in the {@link #typeDefinitions} map.
- * By convention, the {@code null} key is associated to information about the class.
- */
- private Map<String,Info> currentProperties;
-
- /**
- * A single property type under examination, or {@code null} if none.
- * If non-null, this is a value ending with the {@value #PROPERTY_TYPE_SUFFIX} suffix.
- */
- private String currentPropertyType;
-
- /**
- * Default value for the {@code required} attribute of {@link XmlElement}. This default value should
- * be {@code true} for properties declared inside a {@code <sequence>} element, and {@code false} for
- * properties declared inside a {@code <choice>} element.
- */
- private boolean requiredByDefault;
-
- /**
- * Namespace of the type or properties being defined.
- * This is specified by {@code <xs:schema targetNamespace="(…)">}.
- */
- private String targetNamespace;
--
-- /**
-- * The namespaces associated to prefixes, as declared by JAXB {@link XmlNs} annotations.
-- * Used for verifying that no prefix is defined twice for different namespaces.
-- *
-- * <p>This field is not really related to schema loading process. But we keep it in this class for
-- * {@link PackageVerifier} convenience, as a way to share a single map for all verifier instances.</p>
-- */
-- final Map<String,String> allXmlNS;
--
-- /**
-- * Creates a new verifier for classes under the given directory. The given directory shall be the
-- * root of {@code "*.class"} files. For example if the {@code mypackage.MyClass} class is compiled
-- * in the {@code "MyProject/target/classes/mypackage/MyClass.class"} file, then the root directory
-- * shall be {@code "MyProject/target/classes/"}.
-- *
-- * @param classRootDirectory the root of compiled class files.
-- * @param schemaRootDirectory if the computer contains a local copy of ISO schemas, path to that directory.
-- * Otherwise {@code null}. This is only for making tests faster.
-- */
-- public SchemaCompliance(final Path classRootDirectory, final Path schemaRootDirectory) {
- this.classRootDirectory = classRootDirectory;
- this.schemaRootDirectory = schemaRootDirectory;
- factory = DocumentBuilderFactory.newInstance();
- factory.setNamespaceAware(true);
- buffer = new StringBuilder(100);
- typeDefinitions = new HashMap<>();
- schemaLocations = new ArrayDeque<>();
- super(schemaRootDirectory, new Departures(), DocumentationStyle.NONE);
- this.classRootDirectory = classRootDirectory;
-- allXmlNS = new HashMap<>();
-- }
--
-- /**
-- * Verifies {@link XmlElement} annotations on all {@code *.class} files in the given directory and sub-directories.
-- * The given directory must be a sub-directory of the root directory given at construction time.
-- * This method will invoke itself for scanning sub-directories.
-- *
-- * @param directory the directory to scan for classes, relative to class root directory.
-- * @throws IOException if an error occurred while reading files or schemas.
-- * @throws ClassNotFoundException if an error occurred while loading a {@code "*.class"} file.
-- * @throws ParserConfigurationException if {@link javax.xml.parsers.DocumentBuilder} can not be created.
-- * @throws SAXException if an error occurred while parsing the XSD file.
-- * @throws SchemaException if a XSD file does not comply with our assumptions,
-- * or a JAXB annotation failed a compliance check.
-- */
-- public void verify(final Path directory)
-- throws IOException, ClassNotFoundException, ParserConfigurationException, SAXException, SchemaException
-- {
-- PackageVerifier verifier = null;
- final StringBuilder buffer = new StringBuilder();
-- try (DirectoryStream<Path> stream = Files.newDirectoryStream(classRootDirectory.resolve(directory))) {
-- for (Path path : stream) {
-- final String filename = path.getFileName().toString();
-- if (!filename.startsWith(".")) {
-- if (Files.isDirectory(path)) {
-- verify(path);
-- } else if (filename.endsWith(".class")) {
-- path = classRootDirectory.relativize(path);
-- buffer.setLength(0);
-- buffer.append(path.toString()).setLength(buffer.length() - 6); // Remove ".class" suffix.
-- StringBuilders.replace(buffer, '/', '.');
-- final Class<?> c = Class.forName(buffer.toString());
-- if (verifier == null) {
-- verifier = new PackageVerifier(this, c.getPackage());
-- }
-- verifier.verify(c);
-- }
-- }
-- }
-- } catch (DirectoryIteratorException e) {
-- throw e.getCause();
-- }
-- if (verifier != null) {
-- verifier.reportUnused();
- }
- }
-
- /**
- * Loads the XSD file at the given URL. Definitions are stored in the {@link #typeDefinitions} map.
- * Only information of interest are stored, and we assume that the XSD follows OGC/ISO conventions.
- * This method may be invoked recursively if the XSD contains {@code <xs:include>} elements.
- *
- * @param location URL to the XSD file to load.
- */
- final void loadSchema(String location)
- throws IOException, ParserConfigurationException, SAXException, SchemaException
- {
- if (schemaRootDirectory != null && location.startsWith(SCHEMA_ROOT_DIRECTORY)) {
- location = schemaRootDirectory.resolve(location.substring(SCHEMA_ROOT_DIRECTORY.length())).toUri().toString();
- }
- if (!schemaLocations.contains(location)) {
- final Document doc;
- try (final InputStream in = new URL(location).openStream()) {
- doc = factory.newDocumentBuilder().parse(in);
- }
- schemaLocations.addLast(location);
- storeClassDefinition(doc);
- }
- }
-
- /**
- * Stores information about classes in the given node and children. This method invokes itself
- * for scanning children, until we reach sub-nodes about properties (in which case we continue
- * with {@link #storePropertyDefinition(Node)}).
- */
- private void storeClassDefinition(final Node node)
- throws IOException, ParserConfigurationException, SAXException, SchemaException
- {
- if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(node.getNamespaceURI())) {
- switch (node.getNodeName()) {
- case "schema": {
- targetNamespace = getMandatoryAttribute(node, "targetNamespace").intern();
- break;
- }
- /*
- * <xs:include schemaLocation="(…).xsd">
- * Load the schema at the given URL, which is assumed relative.
- */
- case "include": {
- final String oldTarget = targetNamespace;
- final String location = schemaLocations.getLast();
- buffer.setLength(0);
- buffer.append(location, 0, location.lastIndexOf('/') + 1).append(getMandatoryAttribute(node, "schemaLocation"));
- loadSchema(buffer.toString());
- targetNamespace = oldTarget;
- return; // Skip children (normally, there is none).
- }
- /*
- * <xs:element name="(…)" type="(…)_Type">
- * Verify that the names comply with our assumptions.
- */
- case "element": {
- final String name = getMandatoryAttribute(node, "name");
- final String type = getMandatoryAttribute(node, "type");
- if (CODELIST_TYPE.equals(type)) {
- final Map<String,Info> properties = new HashMap<>(4);
- final Info info = new Info(null, targetNamespace, false, false);
- properties.put(null, info); // Remember namespace of the code list.
- properties.put(name, info); // Pseudo-property used in our CodeList adapters.
- if (typeDefinitions.put(name, properties) != null) {
- throw new SchemaException(String.format("Code list \"%s\" is defined twice.", name));
- }
- } else {
- verifyNamingConvention(schemaLocations.getLast(), name, type, TYPE_SUFFIX);
- preparePropertyDefinitions(type);
- addProperty(null, type, false, false);
- currentProperties = null;
- }
- return; // Ignore children (they are about documentation).
- }
- /*
- * <xs:complexType name="(…)_Type">
- * <xs:complexType name="(…)_PropertyType">
- */
- case "complexType": {
- String name = getMandatoryAttribute(node, "name");
- if (name.endsWith(PROPERTY_TYPE_SUFFIX)) {
- currentPropertyType = name;
- verifyPropertyType(node);
- currentPropertyType = null;
- } else {
- /*
- * In the case of "(…)_PropertyType", replace some ISO 19115-2 types by ISO 19115-1 types.
- * For example "MI_Band_Type" is renamed as "MD_Band_Type". We do that because we use only
- * one class for representing those two distincts ISO types. Note that not all ISO 19115-2
- * types extend an ISO 19115-1 type, so we need to apply a case-by-case approach.
- */
- requiredByDefault = true;
- name = TYPES_TO_MERGE.getOrDefault(name, name);
- preparePropertyDefinitions(name);
- storePropertyDefinition(node);
- currentProperties = null;
- }
- return; // Skip children since they have already been examined.
- }
- }
- }
- for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) {
- storeClassDefinition(child);
- }
- }
-
- /**
- * Stores information about properties in the current class. The {@link #currentProperties} field must be
- * set to the map of properties for the class defined by the enclosing {@code <xs:complexType>} element.
- * This method parses elements of the following form:
- *
- * {@preformat xml
- * <xs:element name="(…)" type="(…)_PropertyType" minOccurs="(…)" maxOccurs="(…)">
- * }
- */
- private void storePropertyDefinition(final Node node) throws SchemaException {
- if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(node.getNamespaceURI())) {
- switch (node.getNodeName()) {
- case "sequence": {
- requiredByDefault = true;
- break;
- }
- case "choice": {
- requiredByDefault = false;
- break;
- }
- case "element": {
- boolean isRequired = requiredByDefault;
- boolean isCollection = false;
- final NamedNodeMap attributes = node.getAttributes();
- if (attributes != null) {
- Node attr = attributes.getNamedItem("minOccurs");
- if (attr != null) {
- final String value = attr.getNodeValue();
- if (value != null) {
- isRequired = Integer.parseInt(value) > 0;
- }
- }
- attr = attributes.getNamedItem("maxOccurs");
- if (attr != null) {
- final String value = attr.getNodeValue();
- if (value != null) {
- isCollection = value.equals("unbounded") || Integer.parseInt(value) > 1;
- }
- }
- }
- addProperty(getMandatoryAttribute(node, "name").intern(),
- trim(getMandatoryAttribute(node, "type"), PROPERTY_TYPE_SUFFIX).intern(), isRequired, isCollection);
- return;
- }
- }
- }
- for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) {
- storePropertyDefinition(child);
- }
- }
-
- /**
- * Verifies the naming convention of property defined by the given node. The {@link #currentPropertyType}
- * field must be set to the type of the property defined by the enclosing {@code <xs:complexType>} element.
- * This method parses elements of the following form:
- *
- * {@preformat xml
- * <xs:element ref="(…)">
- * }
- */
- private void verifyPropertyType(final Node node) throws SchemaException {
- if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(node.getNamespaceURI())) {
- if ("element".equals(node.getNodeName())) {
- verifyNamingConvention(schemaLocations.getLast(),
- getMandatoryAttribute(node, "ref"), currentPropertyType, PROPERTY_TYPE_SUFFIX);
- return;
- }
- }
- for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) {
- verifyPropertyType(child);
-- }
-- }
--
-- /**
-- * Verifies that the relationship between the name of the given entity and its type are consistent with
-- * OGC/ISO conventions. This method ignores the prefix (e.g. {@code "mdb:"} in {@code "mdb:MD_Metadata"}).
-- *
-- * @param enclosing schema or other container where the error happened.
-- * @param name the class or property name. Example: {@code "MD_Metadata"}, {@code "citation"}.
-- * @param type the type of the above named object. Example: {@code "MD_Metadata_Type"}, {@code "CI_Citation_PropertyType"}.
-- * @param suffix the expected suffix at the end of {@code type}.
-- * @throws SchemaException if the given {@code name} and {@code type} are not compliant with expected convention.
-- */
-- static void verifyNamingConvention(final String enclosing,
-- final String name, final String type, final String suffix) throws SchemaException
-- {
-- if (type.endsWith(suffix)) {
-- int nameStart = name.indexOf(PREFIX_SEPARATOR) + 1; // Skip "mdb:" or similar prefix.
-- int typeStart = type.indexOf(PREFIX_SEPARATOR) + 1;
-- final int plg = ABSTRACT_PREFIX.length();
-- if (name.regionMatches(nameStart, ABSTRACT_PREFIX, 0, plg)) nameStart += plg;
-- if (type.regionMatches(typeStart, ABSTRACT_PREFIX, 0, plg)) typeStart += plg;
-- final int length = name.length() - nameStart;
-- if (type.length() - typeStart - suffix.length() == length &&
-- type.regionMatches(typeStart, name, nameStart, length))
-- {
-- return;
-- }
-- }
-- throw new SchemaException(String.format("Error in %s:%n" +
-- "The type name should be the name with \"%s\" suffix, but found name=\"%s\" and type=\"%s\">.",
-- enclosing, suffix, name, type));
- }
-
- /**
- * Adds a property of the current name and type. This method is invoked during schema parsing.
- * The property namespace is assumed to be {@link #targetNamespace}.
- */
- private void addProperty(final String name, final String type, final boolean isRequired, final boolean isCollection) throws SchemaException {
- final Info info = new Info(type, targetNamespace, isRequired, isCollection);
- final Info old = currentProperties.put(name, info);
- if (old != null && !old.equal(info)) {
- throw new SchemaException(String.format("Error while parsing %s:%n" +
- "Property \"%s\" is associated to type \"%s\", but that property was already associated to \"%s\".",
- schemaLocations.getLast(), name, type, old));
- }
-- }
--
-- /**
-- * Removes leading and trailing spaces if any, then the prefix and the suffix in the given name.
-- * The prefix is anything before the first {@value #PREFIX_SEPARATOR} character.
-- * The suffix must be the given string, otherwise an exception is thrown.
-- *
-- * @param name the name from which to remove prefix and suffix.
-- * @param suffix the suffix to remove.
-- * @return the given name without prefix and suffix.
-- * @throws SchemaException if the given name does not end with the given suffix.
-- */
-- static String trim(String name, final String suffix) throws SchemaException {
-- name = name.trim();
-- if (name.endsWith(suffix)) {
-- return name.substring(name.indexOf(PREFIX_SEPARATOR) + 1, name.length() - suffix.length());
-- }
-- throw new SchemaException(String.format("Expected a name ending with \"%s\" but got \"%s\".", suffix, name));
- }
-
- /**
- * Returns the attribute of the given name in the given node,
- * or throws an exception if the attribute is not present.
- */
- private static String getMandatoryAttribute(final Node node, final String name) throws SchemaException {
- final NamedNodeMap attributes = node.getAttributes();
- if (attributes != null) {
- final Node attr = attributes.getNamedItem(name);
- if (attr != null) {
- final String value = attr.getNodeValue();
- if (value != null) {
- return value;
- }
- }
- }
- throw new SchemaException(String.format("Node \"%s\" should have a '%s' attribute.", node.getNodeName(), name));
- }
-
- /**
- * Returns the type definitions for a class of the given name.
- *
- * @param className ISO identifier of a class (e.g. {@code "MD_Metadata"}).
- */
- final Map<String, SchemaCompliance.Info> typeDefinition(final String className) {
- return typeDefinitions.get(className);
-- }
--}
diff --cc core/sis-metadata/src/test/java/org/apache/sis/xml/CharSequenceSubstitutionTest.java
index 063cc36,025bad7..a67a265
--- a/core/sis-metadata/src/test/java/org/apache/sis/xml/CharSequenceSubstitutionTest.java
+++ b/core/sis-metadata/src/test/java/org/apache/sis/xml/CharSequenceSubstitutionTest.java
@@@ -21,8 -21,8 +21,8 @@@ import org.opengis.metadata.citation.Ad
import org.opengis.metadata.acquisition.Instrument;
import org.opengis.metadata.identification.DataIdentification;
import org.opengis.metadata.identification.InitiativeType;
+import org.apache.sis.metadata.iso.DefaultIdentifier;
- import org.apache.sis.internal.jaxb.LegacyNamespaces;
+ import org.apache.sis.internal.xml.LegacyNamespaces;
import org.apache.sis.internal.metadata.SensorType;
import org.apache.sis.util.iso.Types;
import org.apache.sis.test.DependsOnMethod;
@@@ -87,15 -84,15 +87,15 @@@ public final strictfp class CharSequenc
" xmlns:gcx=\"" + Namespaces.GCX + '"' +
" xmlns:gco=\"" + Namespaces.GCO + '"' +
" xmlns:xlink=\"" + Namespaces.XLINK + "\">\n" +
- " <mdb:code>\n" +
+ " <mcc:code>\n" +
" <gcx:Anchor xlink:href=\"SDN:L101:2:4326\">EPSG:4326</gcx:Anchor>\n" +
- " </mdb:code>\n" +
- " <mdb:codeSpace>\n" +
+ " </mcc:code>\n" +
+ " <mcc:codeSpace>\n" +
" <gco:CharacterString>L101</gco:CharacterString>\n" +
- " </mdb:codeSpace>\n" +
- "</mdb:MD_Identifier>";
+ " </mcc:codeSpace>\n" +
+ "</mcc:MD_Identifier>";
- final Identifier id = unmarshal(Identifier.class, expected);
+ final DefaultIdentifier id = unmarshal(DefaultIdentifier.class, expected);
assertEquals("codespace", "L101", id.getCodeSpace());
assertEquals("code", "EPSG:4326", id.getCode());
}
diff --cc core/sis-metadata/src/test/java/org/apache/sis/xml/RenameListGenerator.java
index 1c5fb4b,98d6c26..0000000
deleted file mode 100644,100644
--- a/core/sis-metadata/src/test/java/org/apache/sis/xml/RenameListGenerator.java
+++ /dev/null
@@@ -1,213 -1,213 +1,0 @@@
--/*
-- * Licensed to the Apache Software Foundation (ASF) under one or more
-- * contributor license agreements. See the NOTICE file distributed with
-- * this work for additional information regarding copyright ownership.
-- * The ASF licenses this file to You under the Apache License, Version 2.0
-- * (the "License"); you may not use this file except in compliance with
-- * the License. You may obtain a copy of the License at
-- *
-- * http://www.apache.org/licenses/LICENSE-2.0
-- *
-- * Unless required by applicable law or agreed to in writing, software
-- * distributed under the License is distributed on an "AS IS" BASIS,
-- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- * See the License for the specific language governing permissions and
-- * limitations under the License.
-- */
--package org.apache.sis.xml;
--
--import java.util.Map;
--import java.util.Set;
--import java.util.TreeMap;
--import java.util.TreeSet;
--import java.nio.file.Path;
--import java.nio.file.Files;
--import java.nio.file.DirectoryStream;
--import java.nio.file.DirectoryIteratorException;
--import java.io.IOException;
--import java.util.Arrays;
--import java.util.HashSet;
--import java.util.Collections;
--import java.lang.reflect.Method;
--import javax.xml.bind.annotation.XmlSchema;
--import javax.xml.bind.annotation.XmlElement;
--import javax.xml.bind.annotation.XmlRootElement;
- import org.apache.sis.test.xml.SchemaException;
- import org.apache.sis.internal.jaxb.LegacyNamespaces;
-import org.opengis.geoapi.SchemaException;
-import org.apache.sis.internal.xml.LegacyNamespaces;
--
--
--/**
-- * Creates the {@value TransformingReader#FILENAME} file. This class needs to be executed only when the content
-- * has changed, or for verifying the current file. Output format contains namespaces first, then classes,
-- * then properties. Example:
-- *
-- * {@preformat
-- * http://standards.iso.org/iso/19115/-3/cit/1.0
-- * CI_Address
-- * administrativeArea
-- * city
-- * CI_Citation
-- * citedResponsibleParty
-- * }
-- */
--public final class RenameListGenerator {
-- /**
-- * Properties in those namespaces do not have older namespaces to map from.
-- */
-- private static final Set<String> LEGACY_NAMESPACES = Collections.unmodifiableSet(new HashSet<>(
-- Arrays.asList(LegacyNamespaces.GMD, LegacyNamespaces.GMI, LegacyNamespaces.SRV)));
--
-- /**
-- * The {@value} string used in JAXB annotations for default names or namespaces.
-- */
-- private static final String DEFAULT = "##default";
--
-- /**
-- * Root directory from which to search for classes.
-- */
-- private final Path classRootDirectory;
--
-- /**
-- * The content to write. Keys in the first (outer) map are namespaces. Keys in the enclosed maps
-- * are class names. Keys in the enclosed set are property names.
-- */
-- private final Map<String, Map<String, Set<String>>> content;
--
-- /**
-- * Creates a new {@value TransformingReader#FILENAME} generator for classes under the given directory.
-- * The given directory shall be the root of {@code "*.class"} files.
-- *
-- * @param classRootDirectory the root of compiled class files.
-- */
-- public RenameListGenerator(final Path classRootDirectory) {
-- this.classRootDirectory = classRootDirectory;
-- content = new TreeMap<>();
-- }
--
-- /**
-- * Gets the namespaces, types and properties for all class files in the given directory and sub-directories.
-- * Those information are memorized for future listing with {@link #print(Appendable)}.
-- *
-- * @param directory the directory to scan for classes, relative to class root directory.
-- * @throws IOException if an error occurred while reading files or schemas.
-- * @throws ClassNotFoundException if an error occurred while loading a {@code "*.class"} file.
-- * @throws SchemaException if two properties have the same name in the same class and namespace.
-- */
-- public void add(final Path directory) throws IOException, ClassNotFoundException, SchemaException {
-- try (DirectoryStream<Path> stream = Files.newDirectoryStream(classRootDirectory.resolve(directory))) {
-- for (Path path : stream) {
-- final String filename = path.getFileName().toString();
-- if (!filename.startsWith(".")) {
-- if (Files.isDirectory(path)) {
-- add(path);
-- } else if (filename.endsWith(".class")) {
-- path = classRootDirectory.relativize(path);
-- String classname = path.toString();
-- classname = classname.substring(0, classname.length() - 6).replace('/', '.');
-- add(Class.forName(classname));
-- }
-- }
-- }
-- } catch (DirectoryIteratorException e) {
-- throw e.getCause();
-- }
-- }
--
-- /**
-- * Gets the namespaces, types and properties for the given class.
-- * Properties defined in super-classes will be copied as if they were declared in-line.
-- * Those information are memorized for future listing with {@link #print(Appendable)}.
-- *
-- * @throws SchemaException if two properties have the same name in the same class and namespace.
-- */
-- private void add(Class<?> classe) throws SchemaException {
-- XmlRootElement root = classe.getDeclaredAnnotation(XmlRootElement.class);
-- if (root != null) {
-- /*
-- * Add the following entry:
-- *
-- * http://a.namespace
-- * PX_AClass
-- * …
-- *
-- * Then list all properties below "PX_AClass". Note that the namespace may change because properties
-- * may be declared in different namespaces, but the class name stay the same. If the same properties
-- * are inherited by many classes, they will be repeated in each subclass.
-- */
-- final String topLevelTypeName = root.name();
-- String classNS = namespace(classe, root.namespace());
-- for (;; classNS = namespace(classe, root.namespace())) {
-- for (final Method method : classe.getDeclaredMethods()) {
-- if (!method.isBridge()) {
-- final XmlElement xe = method.getDeclaredAnnotation(XmlElement.class);
-- if (xe != null) {
-- String namespace = xe.namespace();
-- if (namespace.equals(DEFAULT)) {
-- namespace = classNS;
-- }
-- add(namespace, topLevelTypeName, xe.name());
-- }
-- }
-- }
-- classe = classe.getSuperclass();
-- root = classe.getDeclaredAnnotation(XmlRootElement.class);
-- if (root == null) break;
-- }
-- } else {
-- /*
-- * In Apache SIS implementation, classes without JAXB annotation except on a single method are
-- * code lists or enumerations. Those classes have exactly one method annotated with @XmlElement,
-- * and that method actually gives a type, not a property (because of the way OGC/ISO wrap every
-- * properties in a type).
-- */
-- XmlElement singleton = null;
-- for (final Method method : classe.getDeclaredMethods()) {
-- final XmlElement xe = method.getDeclaredAnnotation(XmlElement.class);
-- if (xe != null) {
-- if (singleton != null) return;
-- singleton = xe;
-- }
-- }
-- }
-- }
--
-- private static String namespace(final Class<?> classe, String classNS) {
-- if (classNS.equals(DEFAULT)) {
-- classNS = classe.getPackage().getDeclaredAnnotation(XmlSchema.class).namespace();
-- }
-- return classNS;
-- }
--
-- /**
-- * Adds a property in the given class in the given namespace.
-- */
-- private void add(final String namespace, final String typeName, final String property) throws SchemaException {
-- if (!LEGACY_NAMESPACES.contains(namespace)) {
-- if (!content.computeIfAbsent(namespace, (k) -> new TreeMap<>())
-- .computeIfAbsent(typeName, (k) -> new TreeSet<>())
-- .add(property))
-- {
-- if (typeName.equals("Integer")) return; // Exception because of GO_Integer and GO_Integer64.
-- throw new SchemaException(String.format("Duplicated property %s.%s in:%n%s", typeName, property, namespace));
-- }
-- }
-- }
--
-- /**
-- * Prints the {@value TransformingReader#FILENAME} file.
-- *
-- * @param out where to print the content.
-- * @throws IOException if an error occurred while printing the content.
-- */
-- public void print(final Appendable out) throws IOException {
-- for (final Map.Entry<String, Map<String, Set<String>>> e : content.entrySet()) {
-- out.append(e.getKey()).append('\n'); // Namespace
-- for (final Map.Entry<String, Set<String>> c : e.getValue().entrySet()) {
-- out.append(' ').append(c.getKey()).append('\n'); // Class
-- for (final String p : c.getValue()) {
-- out.append(" ").append(p).append('\n'); // Property
-- }
-- }
-- }
-- }
--}
diff --cc core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
index 0000000,9d972de..c30c444
mode 000000,100644..100644
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
@@@ -1,0 -1,510 +1,491 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.coverage.grid;
+
+ import java.util.Arrays;
+ import java.util.Optional;
+ import java.io.Serializable;
+ import org.opengis.geometry.DirectPosition;
+ import org.opengis.metadata.spatial.DimensionNameType;
+ import org.opengis.referencing.operation.MathTransform;
+ import org.opengis.referencing.operation.TransformException;
+ import org.apache.sis.util.ArgumentChecks;
+ import org.apache.sis.util.resources.Errors;
+ import org.apache.sis.util.collection.WeakValueHashMap;
+ import org.apache.sis.internal.raster.Resources;
+ import org.apache.sis.geometry.GeneralEnvelope;
+ import org.apache.sis.geometry.Envelopes;
+ import org.apache.sis.geometry.GeneralDirectPosition;
+ import org.apache.sis.io.TableAppender;
+ import org.apache.sis.util.iso.Types;
+
-// Branch-dependent imports
-import org.opengis.coverage.grid.GridEnvelope;
-
+
+ /**
+ * A range of grid coverage coordinates, also known as "grid envelope".
+ * {@code GridExtent} are defined by {@linkplain #getLow() low} coordinates (often all zeros)
+ * and {@linkplain #getHigh() high} coordinates, <strong>inclusive</strong>.
+ * For example a grid with a width of 512 cells can have a low coordinate of 0 and high coordinate of 511.
+ *
+ * <div class="note"><b>Note:</b>
+ * The inclusiveness of {@linkplain #getHigh() high} coordinates come from ISO 19123.
+ * We follow this specification for all getters methods, but developers should keep in mind
+ * that this is the opposite of Java2D usage where {@link java.awt.Rectangle} maximal values are exclusive.</div>
+ *
+ * {@code GridExtent} instances are unmodifiable, so they can be shared between different {@link GridGeometry} instances.
+ *
+ * <div class="note"><b>Upcoming API generalization:</b>
+ * this class may implement the {@code GridEnvelope} interface in a future Apache SIS version.
+ * This is pending <a href="https://github.com/opengeospatial/geoapi/issues/36">GeoAPI update</a>.</div>
+ *
+ * @author Martin Desruisseaux (IRD, Geomatys)
+ * @version 1.0
+ * @since 1.0
+ * @module
+ */
+ public class GridExtent implements Serializable {
+ /**
+ * Serial number for inter-operability with different versions.
+ */
+ private static final long serialVersionUID = -4717353677844056017L;
+
+ /**
+ * Default axis types for the two-dimensional cases.
+ */
+ private static final DimensionNameType[] DEFAULT_TYPES = new DimensionNameType[] {
+ DimensionNameType.COLUMN,
+ DimensionNameType.ROW
+ };
+
+ /**
+ * A pool of shared {@link DimensionNameType} arrays. We use a pool
+ * because a small amount of arrays is shared by most grid extent.
+ */
+ private static final WeakValueHashMap<DimensionNameType[],DimensionNameType[]> POOL = new WeakValueHashMap<>(DimensionNameType[].class);
+
+ /**
+ * Type of each axis (vertical, temporal, …) or {@code null} if unspecified.
+ * If non-null, the array length shall be equal to {@link #getDimension()}.
+ * Any array element may be null if unspecified for that particular axis.
+ * The same array may be shared by many {@code GridExtent} instances.
+ *
+ * @see #getAxisType(int)
+ */
+ private final DimensionNameType[] types;
+
+ /**
+ * Minimum and maximum grid ordinates. The first half contains minimum ordinates (inclusive),
+ * while the last half contains maximum ordinates (<strong>inclusive</strong>). Note that the
+ * later is the opposite of Java2D usage but conform to ISO specification.
+ */
+ private final long[] ordinates;
+
+ /**
+ * Creates a new array of coordinates with the given number of dimensions.
+ *
+ * @throws IllegalArgumentException if the given number of dimensions is excessive.
+ */
+ private static long[] allocate(final int dimension) throws IllegalArgumentException {
+ if (dimension > Integer.MAX_VALUE / 2) {
+ throw new IllegalArgumentException(Errors.format(Errors.Keys.ExcessiveNumberOfDimensions_1, dimension));
+ }
+ return new long[dimension << 1];
+ }
+
+ /**
+ * Checks if ordinate values in the low part are less than or
+ * equal to the corresponding ordinate value in the high part.
+ *
+ * @throws IllegalArgumentException if a coordinate value in the low part is
+ * greater than the corresponding coordinate value in the high part.
+ */
+ private static void checkCoherence(final long[] ordinates) throws IllegalArgumentException {
+ final int dimension = ordinates.length >>> 1;
+ for (int i=0; i<dimension; i++) {
+ final long lower = ordinates[i];
+ final long upper = ordinates[i + dimension];
+ if (lower > upper) {
+ throw new IllegalArgumentException(Resources.format(
+ Resources.Keys.IllegalGridEnvelope_3, i, lower, upper));
+ }
+ }
+ }
+
+ /**
+ * Verifies that the given array (if non-null) contains no duplicated values, then returns a copy of that array.
+ * The returned copy may be shared by many {@code GridExtent} instances. Consequently it shall not be modified.
+ */
+ @SuppressWarnings("ReturnOfCollectionOrArrayField")
+ private static DimensionNameType[] validateAxisTypes(DimensionNameType[] types) {
+ if (types == null) {
+ return null;
+ }
+ if (Arrays.equals(DEFAULT_TYPES, types)) { // Common case verified before POOL synchronized lock.
+ return DEFAULT_TYPES;
+ }
+ DimensionNameType[] shared = POOL.get(types);
+ if (shared == null) {
+ /*
+ * Verify the array only if it was not found in the pool. Arrays in the pool were already validated,
+ * so do not need to be verified again. The check performed here is inefficient (nested loop), but it
+ * should be okay since the arrays are usually small (less than 5 elements) and the checks should not
+ * be done often (because of the pool).
+ */
+ types = types.clone();
+ for (int i=1; i<types.length; i++) {
+ final DimensionNameType t = types[i];
+ if (t != null) {
+ for (int j=i; --j >= 0;) {
+ if (t.equals(types[j])) {
+ throw new IllegalArgumentException(Errors.format(Errors.Keys.DuplicatedElement_1, t));
+ }
+ }
+ }
+ }
+ shared = POOL.putIfAbsent(types, types);
+ if (shared == null) {
+ return types;
+ }
+ }
+ return shared;
+ }
+
+ /**
+ * Creates an initially empty grid envelope with the given number of dimensions.
+ * All grid coordinate values are initialized to zero. This constructor is private
+ * since {@code GridExtent} coordinate values can not be modified by public API.
+ *
+ * @param dimension number of dimensions.
+ * @param axisTypes the axis types, or {@code null} if unspecified.
+ */
+ private GridExtent(final int dimension, final DimensionNameType[] axisTypes) {
+ ordinates = allocate(dimension);
+ types = validateAxisTypes(axisTypes);
+ }
+
+ /**
+ * Creates a new grid extent for an image or matrix of the given size.
+ * The {@linkplain #getLow() low} grid coordinates are zeros and the axis types are
+ * {@link DimensionNameType#COLUMN} and {@link DimensionNameType#ROW ROW} in that order.
+ *
+ * @param width number of pixels in each row.
+ * @param height number of pixels in each column.
+ */
+ public GridExtent(final long width, final long height) {
+ ArgumentChecks.ensureStrictlyPositive("width", width);
+ ArgumentChecks.ensureStrictlyPositive("height", height);
+ ordinates = new long[4];
+ ordinates[2] = width - 1;
+ ordinates[3] = height - 1;
+ types = DEFAULT_TYPES;
+ }
+
+ /**
+ * Constructs a new grid envelope set to the specified coordinates.
+ * The given arrays contain a minimum (inclusive) and maximum value for each dimension of the grid coverage.
+ * The lowest valid grid coordinates are often zero, but this is not mandatory.
+ * As a convenience for this common case, a null {@code low} array means that all low coordinates are zero.
+ *
+ * <p>An optional (nullable) {@code axisTypes} argument can be used for attaching a label to each grid axis.
+ * For example if this {@code GridExtent} is four-dimensional, then the axis types may be
+ * {{@linkplain DimensionNameType#COLUMN column} (<var>x</var>),
+ * {@linkplain DimensionNameType#ROW row} (<var>y</var>),
+ * {@linkplain DimensionNameType#VERTICAL vertical (<var>z</var>),
+ * {@linkplain DimensionNameType#TIME time} (<var>t</var>)},
+ * which means that the last axis is for the temporal dimension, the third axis is for the vertical dimension, <i>etc.</i>
+ * This information is related to the "real world" coordinate reference system axes, but not necessarily in the same order;
+ * it is caller responsibility to ensure that the grid axes are consistent with the CRS axes.
+ * The {@code axisTypes} array shall not contain duplicated elements,
+ * but may contain {@code null} elements if the type of some axes are unknown.</p>
+ *
+ * @param axisTypes the type of each grid axis, or {@code null} if unspecified.
+ * @param low the valid minimum grid coordinate (always inclusive), or {@code null} for all zeros.
+ * @param high the valid maximum grid coordinate, inclusive or exclusive depending on the next argument.
+ * @param isHighIncluded {@code true} if the {@code high} values are inclusive (as in ISO 19123 specification),
+ * or {@code false} if they are exclusive (as in Java2D usage).
+ * This argument does not apply to {@code low} values, which are always inclusive.
+ * @throws IllegalArgumentException if a coordinate value in the low part is
+ * greater than the corresponding coordinate value in the high part.
+ *
+ * @see #getLow()
+ * @see #getHigh()
+ */
+ public GridExtent(final DimensionNameType[] axisTypes, final long[] low, final long[] high, final boolean isHighIncluded) {
+ ArgumentChecks.ensureNonNull("high", high);
+ final int dimension = high.length;
+ if (low != null && low.length != dimension) {
+ throw new IllegalArgumentException(Errors.format(Errors.Keys.MismatchedDimension_2, low.length, dimension));
+ }
+ if (axisTypes != null && axisTypes.length != dimension) {
+ throw new IllegalArgumentException(Errors.format(Errors.Keys.MismatchedArrayLengths));
+ }
+ ordinates = allocate(dimension);
+ if (low != null) {
+ System.arraycopy(low, 0, ordinates, 0, dimension);
+ }
+ System.arraycopy(high, 0, ordinates, dimension, dimension);
+ if (!isHighIncluded) {
+ for (int i=dimension; i < ordinates.length; i++) {
+ ordinates[i] = Math.decrementExact(ordinates[i]);
+ }
+ }
+ checkCoherence(ordinates);
+ types = validateAxisTypes(axisTypes);
+ }
+
+ /**
+ * Creates a new grid envelope as a copy of the given one.
+ *
+ * @param extent the grid envelope to copy.
+ * @throws IllegalArgumentException if a coordinate value in the low part is
+ * greater than the corresponding coordinate value in the high part.
+ */
- protected GridExtent(final GridEnvelope extent) {
++ protected GridExtent(final GridExtent extent) {
+ ArgumentChecks.ensureNonNull("extent", extent);
+ final int dimension = extent.getDimension();
+ ordinates = allocate(dimension);
+ for (int i=0; i<dimension; i++) {
+ ordinates[i] = extent.getLow(i);
+ ordinates[i + dimension] = extent.getHigh(i);
+ }
+ checkCoherence(ordinates);
- types = (extent instanceof GridExtent) ? ((GridExtent) extent).types : null;
- }
-
- /**
- * Returns the given grid envelope as a {@code GridExtent} implementation.
- * If the given extent is already a {@code GridExtent} instance or is null, then it is returned as-is.
- * Otherwise a new extent is created using the {@linkplain #GridExtent(GridEnvelope) copy constructor}.
- *
- * @param extent the grid envelope to cast or copy, or {@code null}.
- * @return the grid envelope as a {@code GridExtent}, or {@code null} if the given extent was null.
- */
- public static GridExtent castOrCopy(final GridEnvelope extent) {
- if (extent == null || extent instanceof GridExtent) {
- return (GridExtent) extent;
- } else {
- return new GridExtent(extent);
- }
++ types = extent.types;
+ }
+
+ /**
+ * Returns the number of dimensions.
+ *
+ * @return the number of dimensions.
+ */
+ public final int getDimension() {
+ return ordinates.length >>> 1;
+ }
+
+ /**
+ * Returns the valid minimum grid coordinates, inclusive.
+ * The sequence contains a minimum value for each dimension of the grid coverage.
+ *
+ * @return the valid minimum grid coordinates, inclusive.
+ *
+ * @todo Pending resolution of <a href="https://github.com/opengeospatial/geoapi/issues/36">GeoAPI update</a>
+ * before to become public API, in order to use the interface in return type.
+ */
+ GridCoordinatesView getLow() {
+ return new GridCoordinatesView(ordinates, 0);
+ }
+
+ /**
+ * Returns the valid maximum grid coordinates, <strong>inclusive</strong>.
+ * The sequence contains a maximum value for each dimension of the grid coverage.
+ *
+ * @return the valid maximum grid coordinates, <strong>inclusive</strong>.
+ *
+ * @todo Pending resolution of <a href="https://github.com/opengeospatial/geoapi/issues/36">GeoAPI update</a>
+ * before to become public API, in order to use the interface in return type.
+ */
+ GridCoordinatesView getHigh() {
+ return new GridCoordinatesView(ordinates, getDimension());
+ }
+
+ /**
+ * Returns the valid minimum inclusive grid coordinate along the specified dimension.
+ *
+ * @param index the dimension for which to obtain the coordinate value.
+ * @return the low coordinate value at the given dimension, inclusive.
+ * @throws IndexOutOfBoundsException if the given index is negative or is equals or greater
+ * than the {@linkplain #getDimension() grid dimension}.
+ *
+ * @see #getLow()
+ * @see #getHigh(int)
+ */
+ public long getLow(final int index) {
+ ArgumentChecks.ensureValidIndex(getDimension(), index);
+ return ordinates[index];
+ }
+
+ /**
+ * Returns the valid maximum <strong>inclusive</strong> grid coordinate along the specified dimension.
+ *
+ * @param index the dimension for which to obtain the coordinate value.
+ * @return the high coordinate value at the given dimension, <strong>inclusive</strong>.
+ * @throws IndexOutOfBoundsException if the given index is negative or is equals or greater
+ * than the {@linkplain #getDimension() grid dimension}.
+ *
+ * @see #getHigh()
+ * @see #getLow(int)
+ */
+ public long getHigh(final int index) {
+ final int dimension = getDimension();
+ ArgumentChecks.ensureValidIndex(dimension, index);
+ return ordinates[index + dimension];
+ }
+
+ /**
+ * Returns the number of integer grid coordinates along the specified dimension.
+ * This is equal to {@code getHigh(dimension) - getLow(dimension) + 1}.
+ *
+ * @param index the dimension for which to obtain the size.
+ * @return the number of integer grid coordinates along the given dimension.
+ * @throws IndexOutOfBoundsException if the given index is negative or is equals or greater
+ * than the {@linkplain #getDimension() grid dimension}.
+ * @throws ArithmeticException if the size is too large for the {@code long} primitive type.
+ *
+ * @see #getLow(int)
+ * @see #getHigh(int)
+ */
+ public long getSize(final int index) {
+ final int dimension = getDimension();
+ ArgumentChecks.ensureValidIndex(dimension, index);
+ return Math.incrementExact(Math.subtractExact(ordinates[dimension + index], ordinates[index]));
+ }
+
+ /**
+ * Returns the grid coordinates of a representative point.
+ * This point may be used for estimating a {@linkplain GridGeometry#getResolution(boolean) grid resolution}.
+ * The default implementation returns the median (or center) coordinates of this grid extent,
+ * but subclasses can override this method if another point is considered more representative.
+ *
+ * @return the grid coordinates of a representative point.
+ */
+ public DirectPosition getCentroid() {
+ final int dimension = getDimension();
+ final GeneralDirectPosition center = new GeneralDirectPosition(dimension);
+ for (int i=0; i<dimension; i++) {
+ center.setOrdinate(i, ((double) ordinates[i] + (double) ordinates[i + dimension] + 1.0) * 0.5);
+ }
+ return center;
+ }
+
+ /**
+ * Returns the type (vertical, temporal, …) of grid axis at given dimension.
+ * This information is provided because the grid axis type can not always be inferred from the context.
+ * Some examples are:
+ *
+ * <ul>
+ * <li>{@code getAxisType(0)} may return {@link DimensionNameType#COLUMN},
+ * {@link DimensionNameType#TRACK TRACK} or {@link DimensionNameType#LINE LINE}.</li>
+ * <li>{@code getAxisType(1)} may return {@link DimensionNameType#ROW},
+ * {@link DimensionNameType#CROSS_TRACK CROSS_TRACK} or {@link DimensionNameType#SAMPLE SAMPLE}.</li>
+ * <li>{@code getAxisType(2)} may return {@link DimensionNameType#VERTICAL}.</li>
+ * <li>{@code getAxisType(3)} may return {@link DimensionNameType#TIME}.</li>
+ * </ul>
+ *
+ * Above are only examples; there is no constraint on axis order. In particular grid axes do not need to be in the same
+ * order than the corresponding {@linkplain GridGeometry#getCoordinateReferenceSystem() coordinate reference system} axes.
+ *
+ * @param index the dimension for which to obtain the axis type.
+ * @return the axis type at the given dimension. May be absent if the type is unknown.
+ * @throws IndexOutOfBoundsException if the given index is negative or is equals or greater
+ * than the {@linkplain #getDimension() grid dimension}.
+ */
+ public Optional<DimensionNameType> getAxisType(final int index) {
+ ArgumentChecks.ensureValidIndex(getDimension(), index);
+ return Optional.ofNullable((types != null) ? types[index] : null);
+ }
+
+ /**
+ * Transforms this grid extent to a "real world" envelope using the given transform.
+ * The transform shall map <em>pixel corner</em> to real world coordinates.
+ *
+ * @param gridToCRS a transform from <em>pixel corner</em> to real world coordinates
+ * @return this grid extent in real world coordinates.
+ */
+ final GeneralEnvelope toCRS(final MathTransform gridToCRS) throws TransformException {
+ final int dimension = getDimension();
+ final GeneralEnvelope envope = new GeneralEnvelope(dimension);
+ for (int i=0; i<dimension; i++) {
+ envope.setRange(i, ordinates[i], ordinates[i + dimension] + 1.0);
+ }
+ return Envelopes.transform(gridToCRS, envope);
+ }
+
+ /**
+ * Returns a new grid envelope that encompass only some dimensions of this grid envelope.
+ * This method copies this grid envelope into a new grid envelope, beginning at dimension
+ * {@code lower} and extending to dimension {@code upper-1} inclusive. Thus the dimension
+ * of the sub grid envelope is {@code upper - lower}.
+ *
+ * @param lower the first dimension to copy, inclusive.
+ * @param upper the last dimension to copy, exclusive.
+ * @return the sub-envelope, or {@code this} if [{@code lower} … {@code upper}] is [0 … {@link #getDimension() dimension}].
+ * @throws IndexOutOfBoundsException if an index is out of bounds.
+ */
+ public GridExtent subExtent(final int lower, final int upper) {
+ final int dimension = getDimension();
+ ArgumentChecks.ensureValidIndexRange(dimension, lower, upper);
+ final int newDim = upper - lower;
+ if (newDim == dimension) {
+ return this;
+ }
+ DimensionNameType[] axisTypes = types;
+ if (axisTypes != null) {
+ axisTypes = Arrays.copyOfRange(axisTypes, lower, upper);
+ }
+ final GridExtent sub = new GridExtent(newDim, axisTypes);
+ System.arraycopy(ordinates, lower, sub.ordinates, 0, newDim);
+ System.arraycopy(ordinates, lower+dimension, sub.ordinates, newDim, newDim);
+ return sub;
+ }
+
+ /**
+ * Returns a hash value for this grid envelope. This value need not remain
+ * consistent between different implementations of the same class.
+ *
+ * @return a hash value for this grid envelope.
+ */
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(ordinates) + Arrays.hashCode(types) ^ (int) serialVersionUID;
+ }
+
+ /**
+ * Compares the specified object with this grid envelope for equality.
+ *
+ * @param object the object to compare with this grid envelope for equality.
+ * @return {@code true} if the given object is equal to this grid envelope.
+ */
+ @Override
+ public boolean equals(final Object object) {
+ if (object != null && object.getClass() == GridExtent.class) {
+ final GridExtent other = (GridExtent) object;
+ return Arrays.equals(ordinates, other.ordinates) && Arrays.equals(types, other.types);
+ }
+ return false;
+ }
+
+ /**
+ * Returns a string representation of this grid envelope. The returned string
+ * is implementation dependent and is provided for debugging purposes only.
+ */
+ @Override
+ public String toString() {
+ final TableAppender table = new TableAppender(" ");
+ final int dimension = getDimension();
+ for (int i=0; i<dimension; i++) {
+ String name;
+ if ((types == null) || (name = Types.getCodeLabel(types[i])) == null) {
+ name = Integer.toString(i);
+ }
+ table.append(name).append(':').nextColumn();
+ table.setCellAlignment(TableAppender.ALIGN_RIGHT);
+ table.append(Long.toString(ordinates[i])).nextColumn();
+ table.append("to").nextColumn();
+ table.append(Long.toString(ordinates[i + dimension])).nextLine();
+ table.setCellAlignment(TableAppender.ALIGN_LEFT);
+ }
+ return table.toString();
+ }
+ }
diff --cc core/sis-raster/src/test/java/org/apache/sis/coverage/grid/PixelTranslationTest.java
index 0000000,e4498f3..f6f4f15
mode 000000,100644..100644
--- a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/PixelTranslationTest.java
+++ b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/PixelTranslationTest.java
@@@ -1,0 -1,124 +1,124 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.coverage.grid;
+
+ import org.opengis.metadata.spatial.PixelOrientation;
+ import org.opengis.referencing.datum.PixelInCell;
+ import org.opengis.referencing.operation.MathTransform;
+ import org.opengis.referencing.operation.TransformException;
+ import org.apache.sis.referencing.operation.transform.MathTransforms;
+ import org.apache.sis.referencing.operation.matrix.Matrix3;
+ import org.apache.sis.referencing.operation.matrix.Matrix4;
+ import org.apache.sis.test.TestCase;
+ import org.junit.Test;
+
-import static org.opengis.test.Assert.*;
++import static org.apache.sis.test.Assert.*;
+
+
+ /**
+ * Tests {@link PixelTranslation}.
+ *
+ * @author Martin Desruisseaux (IRD, Geomatys)
+ * @version 1.0
+ * @since 1.0
+ * @module
+ */
+ public final strictfp class PixelTranslationTest extends TestCase {
+ /**
+ * Returns a transform from center to corner with the given number of dimensions.
+ */
+ private static MathTransform centerToCorner(final int dimension) {
+ return PixelTranslation.translate(MathTransforms.identity(dimension), PixelInCell.CELL_CENTER, PixelInCell.CELL_CORNER);
+ }
+
+ /**
+ * Returns a transform from center to corner in the two-dimensional case.
+ */
+ private static MathTransform centerToCorner2D() {
+ return PixelTranslation.translate(MathTransforms.identity(2), PixelOrientation.CENTER, PixelOrientation.UPPER_LEFT, 0, 1);
+ }
+
+ /**
+ * Tests {@link PixelTranslation#translate(MathTransform, PixelInCell, PixelInCell)} with an identity transform.
+ * If grid coordinates (0,0) in "pixel center" convention map to (0,0) in "real world" coordinates,
+ * then grid coordinates (0,0) in "pixel corner" convention shall map to (-½, -½) in real world.
+ * That way, grid coordinates (½,½) in "pixel corner" convention still map to (0,0) in real world.
+ *
+ * @throws TransformException if an error occurred while transforming a test point.
+ */
+ @Test
+ public void testTranslatePixelInCell() throws TransformException {
+ final MathTransform mt = centerToCorner(3);
+ assertMatrixEquals("center → corner", new Matrix4(
+ 1, 0, 0, -0.5,
+ 0, 1, 0, -0.5,
+ 0, 0, 1, -0.5,
+ 0, 0, 0, 1), MathTransforms.getMatrix(mt), STRICT);
+ /*
+ * Just for making clear what we explained in javadoc comment: the real world (0,0,0) coordinates was in the center
+ * of cell (0,0,0). After we switched to "cell corner" convention, that center is (½,½,½) in grid coordinates but
+ * should still map (0,0,0) in "real world" coordinates.
+ */
+ final double[] coordinates = new double[] {0.5, 0.5, 0.5};
+ mt.transform(coordinates, 0, coordinates, 0, 1);
+ assertArrayEquals(new double[3], coordinates, STRICT);
+ }
+
+ /**
+ * Tests {@link PixelTranslation#translate(MathTransform, PixelOrientation, PixelOrientation, int, int)}.
+ * See {@link #testTranslatePixelInCell()} for discussion on expected values.
+ */
+ @Test
+ public void testTranslatePixelOrientation() {
+ MathTransform mt = centerToCorner2D();
+ assertMatrixEquals("center → corner", new Matrix3(
+ 1, 0, -0.5,
+ 0, 1, -0.5,
+ 0, 0, 1), MathTransforms.getMatrix(mt), STRICT);
+
+ mt = PixelTranslation.translate(MathTransforms.identity(3), PixelOrientation.LOWER_LEFT, PixelOrientation.CENTER, 1, 2);
+ assertMatrixEquals("corner → center", new Matrix4(
+ 1, 0, 0, 0.0,
+ 0, 1, 0, +0.5,
+ 0, 0, 1, -0.5,
+ 0, 0, 0, 1), MathTransforms.getMatrix(mt), STRICT);
+ }
+
+ /**
+ * Verifies consistency of cached transforms when using translations inferred from {@link PixelInCell}.
+ */
+ @Test
+ public void testCache() {
+ MathTransform previous = null;
+ for (int dimension = 1; dimension <= 5; dimension++) {
+ final MathTransform mt = centerToCorner(dimension);
+ assertNotSame("Transforms with different number of dimensions.", previous, mt);
+ assertSame("Transforms with same number of dimensions", mt, centerToCorner(dimension));
+ previous = mt;
+ }
+ }
+
+ /**
+ * Verifies consistency of cached transforms when using translations inferred from {@link PixelOrientation}.
+ */
+ @Test
+ public void testCache2D() {
+ assertSame(centerToCorner(2), centerToCorner2D());
+ assertSame(PixelTranslation.translate(MathTransforms.identity(2), PixelInCell.CELL_CORNER, PixelInCell.CELL_CENTER),
+ PixelTranslation.translate(MathTransforms.identity(2), PixelOrientation.UPPER_LEFT, PixelOrientation.CENTER, 0, 1));
+ }
+ }
diff --cc core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/AbstractLocation.java
index 666d2d3,e8d02c7..cedbe55
--- a/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/AbstractLocation.java
+++ b/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/AbstractLocation.java
@@@ -27,10 -27,11 +27,9 @@@ import org.opengis.util.InternationalSt
import org.apache.sis.util.iso.Types;
import org.apache.sis.geometry.Envelope2D;
import org.apache.sis.geometry.GeneralDirectPosition;
- import org.apache.sis.util.Debug;
// Branch-dependent imports
-import org.opengis.metadata.citation.Party;
-import org.opengis.referencing.gazetteer.Location;
-import org.opengis.referencing.gazetteer.LocationType;
+import org.apache.sis.metadata.iso.citation.AbstractParty;
/**
diff --cc core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java
index ee1496b,23bb329..8cc42b3
--- a/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java
+++ b/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java
@@@ -56,7 -58,7 +56,6 @@@ import org.apache.sis.util.ArgumentChec
import org.apache.sis.util.StringBuilders;
import org.apache.sis.util.Utilities;
import org.apache.sis.util.Workaround;
- import org.apache.sis.util.Debug;
-import org.apache.sis.util.logging.Logging;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.resources.Vocabulary;
import org.apache.sis.geometry.Shapes2D;
diff --cc core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ServicesForMetadata.java
index 496e874,9a2000d..3c402b8
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ServicesForMetadata.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ServicesForMetadata.java
@@@ -53,6 -53,7 +53,7 @@@ import org.opengis.referencing.operatio
import org.opengis.referencing.operation.SingleOperation;
import org.opengis.referencing.operation.CoordinateOperation;
import org.opengis.referencing.operation.CoordinateOperationFactory;
-import org.opengis.metadata.Identifier;
++import org.opengis.referencing.ReferenceIdentifier;
import org.opengis.metadata.citation.Citation;
import org.opengis.metadata.citation.OnLineFunction;
import org.opengis.metadata.citation.OnlineResource;
@@@ -97,16 -98,8 +98,17 @@@ import org.apache.sis.util.resources.Vo
import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.Exceptions;
import org.apache.sis.util.Utilities;
+ import org.apache.sis.util.iso.DefaultNameSpace;
+// Branch-dependent imports
+import org.apache.sis.metadata.iso.citation.DefaultCitation;
+import org.apache.sis.referencing.factory.GeodeticObjectFactory;
+import org.apache.sis.referencing.cs.DefaultParametricCS;
+import org.apache.sis.referencing.datum.DefaultParametricDatum;
+import org.apache.sis.referencing.factory.InvalidGeodeticParameterException;
+import org.opengis.referencing.datum.Datum;
+import org.opengis.referencing.datum.DatumFactory;
+
/**
* Implements the referencing services needed by the {@code "sis-metadata"} module.
@@@ -420,6 -413,34 +422,34 @@@ public final class ServicesForMetadata
return new DirectPosition2D(CommonCRS.defaultGeographic(), λ, φ);
}
+ /**
+ * Returns an identifier for the given object, giving precedence to EPSG identifier if available.
+ * The returned string should be of the form {@code "AUTHORITY:CODE"} if possible (no guarantees).
+ *
+ * @param object the object for which to get an identifier.
+ * @return an identifier for the given object, with preference given to EPSG codes.
+ * @throws FactoryException if an error occurred while searching for the EPSG code.
+ *
+ * @since 1.0
+ */
+ @Override
+ public String getPreferredIdentifier(final IdentifiedObject object) throws FactoryException {
+ final Integer code = IdentifiedObjects.lookupEPSG(object);
+ if (code != null) {
+ return Constants.EPSG + DefaultNameSpace.DEFAULT_SEPARATOR + code;
+ }
+ /*
+ * If above code did not found an EPSG code, discard EPSG codes that
+ * we may find in the loop below because they are probably invalid.
+ */
- for (final Identifier id : object.getIdentifiers()) {
++ for (final ReferenceIdentifier id : object.getIdentifiers()) {
+ if (!Constants.EPSG.equalsIgnoreCase(id.getCodeSpace())) {
+ return IdentifiedObjects.toString(id);
+ }
+ }
+ return IdentifiedObjects.getSimpleNameOrIdentifier(object);
+ }
+
diff --cc core/sis-referencing/src/main/java/org/apache/sis/referencing/IdentifiedObjects.java
index 0c906a0,24f2e27..ee6df97
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/IdentifiedObjects.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/IdentifiedObjects.java
@@@ -768,12 -772,9 +778,12 @@@ public final class IdentifiedObjects ex
return identifier.toString();
}
final String code = identifier.getCode();
- String cs = identifier.getCodeSpace();
+ String cs = null;
+ if (identifier instanceof ReferenceIdentifier) {
+ cs = ((ReferenceIdentifier) identifier).getCodeSpace();
+ }
if (cs == null || cs.isEmpty()) {
- cs = org.apache.sis.internal.util.Citations.getIdentifier(identifier.getAuthority(), true);
+ cs = Citations.getUnicodeIdentifier(identifier.getAuthority());
}
if (cs != null) {
return cs + DefaultNameSpace.DEFAULT_SEPARATOR + code;
diff --cc core/sis-referencing/src/main/java/org/apache/sis/referencing/NamedIdentifier.java
index 3c50bea,d1a89e9..517acd9
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/NamedIdentifier.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/NamedIdentifier.java
@@@ -122,8 -122,10 +122,10 @@@ public class NamedIdentifier extends Im
* to that name.</p>
*
* @param identifier the identifier to copy.
+ *
- * @see #castOrCopy(Identifier)
++ * @see #castOrCopy(ReferenceIdentifier)
*/
- public NamedIdentifier(final Identifier identifier) {
+ public NamedIdentifier(final ReferenceIdentifier identifier) {
super(identifier);
if (identifier instanceof GenericName) {
name = (GenericName) identifier;
@@@ -137,9 -139,11 +139,11 @@@
* {@link #head()} and {@link #scope()} will delegate to the given name.
*
* @param name the name to wrap.
+ *
+ * @see #castOrCopy(GenericName)
*/
public NamedIdentifier(final GenericName name) {
- super(name instanceof Identifier ? (Identifier) name : new NameToIdentifier(name));
+ super(name instanceof ReferenceIdentifier ? (ReferenceIdentifier) name : new NameToIdentifier(name));
this.name = name;
isNameSupplied = true;
}
@@@ -329,6 -333,60 +333,60 @@@
}
/**
+ * Returns a SIS identifier implementation with the values of the given arbitrary implementation.
+ * This method performs the first applicable action in the following choices:
+ *
+ * <ul>
+ * <li>If the given object is {@code null}, then this method returns {@code null}.</li>
+ * <li>Otherwise if the given object is already an instance of
+ * {@code NamedIdentifier}, then it is returned unchanged.</li>
+ * <li>Otherwise a new {@code NamedIdentifier} instance is created using the
- * {@linkplain #NamedIdentifier(Identifier) copy constructor} and returned.
++ * {@linkplain #NamedIdentifier(ReferenceIdentifier) copy constructor} and returned.
+ * Note that this is a <cite>shallow</cite> copy operation, since the other
+ * metadata contained in the given object are not recursively copied.</li>
+ * </ul>
+ *
+ * @param object the object to get as a SIS implementation, or {@code null} if none.
+ * @return a SIS implementation containing the values of the given object (may be the
+ * given object itself), or {@code null} if the argument was null.
+ *
+ * @since 1.0
+ */
- public static NamedIdentifier castOrCopy(final Identifier object) {
++ public static NamedIdentifier castOrCopy(final ReferenceIdentifier object) {
+ if (object == null || object instanceof NamedIdentifier) {
+ return (NamedIdentifier) object;
+ }
+ return new NamedIdentifier(object);
+ }
+
+ /**
+ * Returns a SIS name implementation with the values of the given arbitrary implementation.
+ * This method performs the first applicable action in the following choices:
+ *
+ * <ul>
+ * <li>If the given object is {@code null}, then this method returns {@code null}.</li>
+ * <li>Otherwise if the given object is already an instance of
+ * {@code NamedIdentifier}, then it is returned unchanged.</li>
+ * <li>Otherwise a new {@code NamedIdentifier} instance is created using the
+ * {@linkplain #NamedIdentifier(GenericName) copy constructor} and returned.
+ * Note that this is a <cite>shallow</cite> copy operation, since the other
+ * metadata contained in the given object are not recursively copied.</li>
+ * </ul>
+ *
+ * @param object the object to get as a SIS implementation, or {@code null} if none.
+ * @return a SIS implementation containing the values of the given object (may be the
+ * given object itself), or {@code null} if the argument was null.
+ *
+ * @since 1.0
+ */
+ public static NamedIdentifier castOrCopy(final GenericName object) {
+ if (object == null || object instanceof NamedIdentifier) {
+ return (NamedIdentifier) object;
+ }
+ return new NamedIdentifier(object);
+ }
+
+ /**
* The last element in the sequence of {@linkplain #getParsedNames() parsed names}.
* By default, this is the same value than the {@linkplain #getCode() code} provided as a local name.
*
diff --cc core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java
index fa58c7a,67ac1d2..c05d871
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java
@@@ -38,13 -38,7 +38,12 @@@ import org.apache.sis.util.iso.Abstract
import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.CharSequences;
import org.apache.sis.util.Classes;
- import org.apache.sis.util.Debug;
+// Branch-dependent imports
+import org.apache.sis.referencing.cs.DefaultParametricCS;
+import org.apache.sis.referencing.crs.DefaultParametricCRS;
+import org.apache.sis.referencing.datum.DefaultParametricDatum;
+
/**
* Creates geodetic objects from codes defined by an authority.
diff --cc core/sis-utility/src/main/java/org/apache/sis/internal/system/Supervisor.java
index e3a7a88,81358b6..3d49ba6
--- a/core/sis-utility/src/main/java/org/apache/sis/internal/system/Supervisor.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/internal/system/Supervisor.java
@@@ -60,9 -60,9 +60,9 @@@ public final class Supervisor extends S
* Whatever JMX agent is enabled. Setting this variable to {@code false} allows the
* Java compiler to omit any dependency to this {@code Supervisor} class.
*
- * @see <a href="http://sis.apache.org/branches.html#master">Differences between SIS master and branches</a>
+ * @see <a href="http://sis.apache.org/source.html#master">Differences between SIS master and branches</a>
*/
- static final boolean ENABLED = true;
+ static final boolean ENABLED = false;
/**
* The JMX object name for the {@code Supervisor} service.
diff --cc core/sis-utility/src/main/java/org/apache/sis/internal/util/TemporalUtilities.java
index 78b952d,9e55e7f..5511255
--- a/core/sis-utility/src/main/java/org/apache/sis/internal/util/TemporalUtilities.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/internal/util/TemporalUtilities.java
@@@ -44,9 -44,9 +44,9 @@@ public final class TemporalUtilities ex
*
* This constant will be removed after SIS release a sis-temporal module.
*
- * @see <a href="http://sis.apache.org/branches.html#master">Differences between SIS master and branches</a>
+ * @see <a href="http://sis.apache.org/source.html#master">Differences between SIS master and branches</a>
*/
- public static final boolean REPORT_MISSING_MODULE = true;
+ public static final boolean REPORT_MISSING_MODULE = false;
/**
* Do not allow instantiation of this class.
diff --cc ide-project/NetBeans/nbproject/genfiles.properties
index 7867cbe3,11e6a4b..e75ddfb
--- a/ide-project/NetBeans/nbproject/genfiles.properties
+++ b/ide-project/NetBeans/nbproject/genfiles.properties
@@@ -3,6 -3,6 +3,6 @@@
build.xml.data.CRC32=58e6b21c
build.xml.script.CRC32=462eaba0
build.xml.stylesheet.CRC32=28e38971@1.53.1.46
- nbproject/build-impl.xml.data.CRC32=846ff049
-nbproject/build-impl.xml.data.CRC32=5c9f3b18
-nbproject/build-impl.xml.script.CRC32=fb8d24ed
++nbproject/build-impl.xml.data.CRC32=e3512adb
+nbproject/build-impl.xml.script.CRC32=3e1070bc
nbproject/build-impl.xml.stylesheet.CRC32=830a3534@1.80.1.48
diff --cc storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CRSBuilder.java
index b347e51,6a2061d..ee458b0
--- a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CRSBuilder.java
+++ b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CRSBuilder.java
@@@ -309,8 -320,9 +322,9 @@@ final class CRSBuilder
* The factory is fetched when first needed.
*
* @return the operation factory (never {@code null}).
+ * @see <a href="https://issues.apache.org/jira/browse/SIS-102">SIS-102</a>
*/
- private CoordinateOperationFactory operationFactory() {
+ private DefaultCoordinateOperationFactory operationFactory() {
if (operationFactory == null) {
operationFactory = CoordinateOperations.factory();
}
diff --cc storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java
index d5c775d,f9d9c9c..1aacd21
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java
@@@ -48,8 -49,9 +49,10 @@@ import org.opengis.referencing.cs.AxisD
import org.opengis.referencing.crs.VerticalCRS;
import org.apache.sis.util.iso.Types;
+import org.apache.sis.util.iso.DefaultNameFactory;
import org.apache.sis.util.iso.SimpleInternationalString;
+ import org.apache.sis.util.logging.WarningListeners;
+ import org.apache.sis.storage.DataStore;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.metadata.iso.DefaultMetadata;
import org.apache.sis.metadata.iso.citation.*;
@@@ -613,14 -632,11 +629,11 @@@ split: while ((start = CharSequences.s
addCitedResponsibleParty(contributor, null);
}
final ResponsibleParty r = createResponsibleParty(PUBLISHER, false);
-- if (r != null) {
++ if (r instanceof DefaultResponsibility) {
addDistributor(r);
- /*
- * TODO: There is some transfert option, etc. that we could set there.
- * See UnidataDD2MI.xsl for options for OPeNDAP, THREDDS, etc.
- */
- publisher = addIfNonNull(publisher, r.getOrganisationName());
- publisher = addIfNonNull(publisher, toInternationalString(r.getIndividualName()));
- for (final Party party : r.getParties()) {
++ for (final AbstractParty party : ((DefaultResponsibility) r).getParties()) {
+ publisher = addIfNonNull(publisher, party.getName());
+ }
}
}
decoder.setSearchPath(searchPath);