Des spécifications de l’OGC aux interfaces Java

Les interfaces Java du projet GeoAPI sont parfois générées à partir d’autres fichiers fournis par l’OGC, tels que les fichiers XSD. Mais il y a toujours une révision manuelle, et très souvent des modifications par rapport aux fichiers générés par des processus automatiques. Il aurait été possible de générer automatiquement des interfaces Java à partir des standards de l’OGC à l’aide d’outils existants. Par exemple une des approches les plus utilisées est de transformer les schémas XSD en interfaces Java à l’aide de l’utilitaire en ligne de commande xjc. Cet utilitaire étant fournit avec la plupart des distributions du Java (il fait partie des outils de JAXB), cette approche est choisie par plusieurs projets que l’on trouve sur internet. D’autres approches utilisent des outils intégrés à l’environnement de développement Eclipse, ou prennent comme point de départ les schémas UML plutôt que XSD. Une approche similaire avait été tentée dans les débuts du projet GeoAPI, mais a été rapidement abandonnée. Nous avons privilégié une approche manuelle pour les raisons suivantes:

Les écarts par rapport aux normes sont documentés dans chaque classe et chaque méthode concernées. Chaque mention d’un écart est aussi recopiée dans une page unique, pour donner une vue d’ensemble. Étant donné que ces écarts brouillent les liens qui existent entre les standards et certaines interfaces Java, la correspondance entre ces langages est explicitée par des annotations @UML et des fichiers de propriétés, décrits dans la section suivante.

Correspondances explicites entre GeoAPI et les spécifications abstraites

Pour chaque classe, méthode et constante définie à partir d’un standard OGC ou ISO, GeoAPI indique sa provenance à l’aide d’annotations définies dans le paquet org.opengis.annotation. En particulier l’annotation @UML indique le standard, le nom de l’élément dans ce standard ainsi que son niveau d’obligation. Par exemple dans l’extrait de code suivant, la première annotation @UML indique que l’interface Java qui la suit (ProjectedCRS) est définie à partir du type SC_ProjectedCRS du standard ISO 19111. La seconde annotation @UML, appliquée cette fois à la méthode getCoordinateSystem(), indique que la méthode est définie à partir de l’association coordinateSystem du standard ISO 19111, et que cette association est obligatoire — ce qui, traduit en Java, signifie que la méthode n’est pas autorisée à retourner la valeur null.

package org.opengis.referencing.crs;

/**
 * A 2D coordinate reference system used to approximate the shape of the earth on a planar surface.
 */
@UML(specification=ISO_19111, identifier="SC_ProjectedCRS")
public interface ProjectedCRS extends GeneralDerivedCRS {
    /**
     * Returns the coordinate system, which must be Cartesian.
     */
    @UML(obligation=MANDATORY, specification=ISO_19111, identifier="coordinateSystem")
    CartesianCS getCoordinateSystem();
}

Les méthodes d’introspections du Java permettent d’accéder à ces informations pendant l’exécution d’une application. C’est utile pour obtenir les noms à afficher à des utilisateurs familiers avec les normes de l’OGC, ou pour écrire des éléments dans un document XML. La classe org.apache.sis.util.iso.Types fournit des méthodes de commodité telles que getStandardName(Class) pour effectuer cette opération. Par exemple le code suivant affichera « Le nom standard du type org.opengis.referencing.crs.ProjectedCRS est SC_ProjectedCRS »:

Class<?> type = ProjectedCRS.class;
System.out.println("Le nom standard du type " + type.getName() + " est " + Types.getStandardName(type));

La méthode de commodité Types.forStandardName(String) effectue l’opération inverse. Les applications qui souhaiteraient effectuer ces opérations sans utiliser les méthodes de commodités de Apache SIS trouveront des indications dans un chapitre séparé.

Correspondances implicites avec le JDK standard

Certaines classes et méthodes n’ont ni annotation @UML, ni entrée dans le fichier class-index.properties. Il s’agit soit d’extensions de GeoAPI, ou soit de types définis dans d’autres bibliothèques, notamment le JDK standard. Pour ce dernier cas, la correspondance avec les standards ISO est implicite. Le tableau suivant décrit cette correspondance pour les types de la norme ISO 19103. Les types primitifs du Java standard sont préférés lorsqu’ils sont applicables, mais parfois leurs équivalents sous forme d’objets sont employés lorsqu’il est nécessaire d’autoriser des valeurs nulles.

Correspondances entre ISO 19103 et JDK
Type ISO Type JDK Remarques
Nombres
Integer int Parfois java.lang.Integer pour les attributs optionnels.
Integer (certains cas) long Parfois java.lang.Long pour les attributs optionnels.
Real double Parfois java.lang.Double pour les attributs optionnels.
Decimal java.math.BigDecimal
Number java.lang.Number
Textes
FreeText (pas d’équivalent) Voir org.opengis.util.InternationalString ci-dessous.
CharacterString java.lang.String Souvent org.opengis.util.InternationalString (voir ci-dessous).
LocalisedCharacterString java.lang.String
Sequence<Character> java.lang.CharSequence
Character char
Dates et heures
Date java.util.Date
Time java.util.Date
DateTime java.util.Date
Collections
Collection java.util.Collection
Bag java.util.Collection Un Bag est similaire à un Set sans la restriction d’unicité.
Set java.util.Set
Sequence java.util.List
Dictionary java.util.Map
KeyValuePair java.util.Map.Entry
Énumérations
Enumeration java.lang.Enum
CodeList (pas d’équivalent) Voir org.opengis.util.CodeList ci-dessous.
Divers
Boolean boolean Parfois java.lang.Boolean pour les attributs optionnels.
Any java.lang.Object

L’équivalent le plus direct de CharacterString est la classe String, mais GeoAPI emploie souvent l’interface InternationalString pour permettre au client de choisir la langue. C’est utile par exemple sur un serveur fournissant simultanément des pages dans plusieurs langues. En reportant les traductions à l’utilisation des objets plutôt qu’au moment de leur création, on permet à la bibliothèque SIS de fournir les mêmes instances de Metadata ou Coverage (par exemple) pour les mêmes données peu importe la langue du client. Les traductions peuvent être faites à la volée à l’aide d’un ResourceBundle de l’application, ou être fournies directement avec les données (cas des Metadata notamment).

Les Enumeration correspondent aux Enum du Java. Ils ont en commun de définir toutes les valeurs autorisées, sans permettre à l’utilisateur d’en ajouter. Les CodeList sont similaires à ces énumérations, excepté que les utilisateurs peuvent y ajouter leurs propres éléments. Le JDK standard n’offrant pas cette possibilité, GeoAPI définit une classe abstraite CodeList reproduisant certaines fonctionnalités de Enum tout en étant extensible. Les extensions s’obtiennent par les méthodes statiques valueOf(String) qui, contrairement à celle de Enum, créeront de nouvelles instances si le nom donné ne correspond pas au nom d’une instance existante.

MediumName cdRom  = MediumName.CD_ROM;
MediumName usbKey = MediumName.valueOf("USB_KEY"); // Aucune constante n’existe pour cette valeur.
assert MediumName.valueOf("CD_ROM")  == cdRom  : "valueOf doit retourner les constantes existantes.";
assert MediumName.valueOf("USB_KEY") == usbKey : "valueOf doit cacher les valeurs précédemment demandées.";