The vast benefit of these specifications is communicated via their included UML diagrams. We use this information to define the names of our interfaces, methods and fields.
Here is an example for us to work through.
Figure A.10 defines metadata about the content of a coverage and the feature catalogue(s) used to define features. The data dictionary for this diagram is located in B.2.8.
+-----------------------+
| MD_ContentInformation |
+-----------------------+
^
|--------------------------------.
| |
+------------------------------------------+ +--------------------------------+
| MD_CoverageDescription | | MD_FeatureCatalogDescription |
|------------------------------------------| |--------------------------------|
| + attributeDescription : RecordType | | complianceCode [0.1] : Boolean |
| + contentType: MD_CoverageContentTypeCode| | ... |
| | +--------------------------------+
+------------------------------------------+
+----------<<CodeList>>------+
| MD_CoverageContentTypeCode |
|----------------------------|
| + image |
| + thematicClassification |
| + physicalMeasurement |
+----------------------------+
Figure A.10 - Content information
You can use this information to quickly define your interfaces and code lists. If you package starts getting crowded use these
diagram names as guides when defining sub packages.
A code list is similar in spirit to a Java 5 enum - but you can extended it with additional values later. Both constructs allow you to write well-defined machine readable text.
Mapping to Java
Here are some other well-known mappings you can follow to make everything consistent.
Optional Field
ISO |
|
Java |
Optional |
|---|---|---|---|
Real |
0:1 |
|
Value may be null |
Integer |
0:1 |
|
Value may be null |
Boolean |
0:1 |
|
Value may be null |
As an example GeographicExtent.getInclusion() is an optional field.
Mandatory Field
ISO |
|
Java |
Mandatory |
|---|---|---|---|
Real |
1:1 |
|
May be null if unknown, yes that is invalid |
Integer |
1:1 |
|
May be null if unknown, yes that is invalid |
Boolean |
1:1 |
|
May be null if unknown |
Invalid (or incomplete) information exist in the field. We use the boxed types (Integer, Double, Boolean) so we can return null to represent missing information. In addition, this approach protect us from ISO 19115 changes in OPTIONAL versus MANDATORY status of attributes.
Mandatory Field with a Default
ISO |
|
Java |
Mandatory with Default |
|---|---|---|---|
Real |
1:1 |
|
Indicate default value in javadocs |
Integer |
1:1 |
|
Indicate default value in javadocs |
Boolean |
1:1 |
|
Indicate default value (true or false) in javadocs |
Filter |
1:1 |
Filter |
Indicate default ( INCLUDES or EXCLUDES) in javadocs |
Here are two examples:
SampleDimension.getOffset()returns anint.FeatureType.isHidden()returns aboolean.
Mandatory Fields as Object Definition
When your manditory attribute completly defines your data structure you are forced to use primitives:
- Interfaces that perform operations cannot function without their full definition, like
MathTransform1D.transform(double). Most referencing and geometry interfaces use primitive types in this manner. - Interfaces that function as Data Objects cannot exist without their full definition, like
RepresentativeFraction.getDenominator().
ISO |
|
Java |
|---|---|---|
Real |
1:1 |
|
Integer |
1:1 |
|
Boolean |
1:1 |
|
Handling of Text
GeoAPI provides some additional data objects for representing text (beyond the usual String and Enum provided by Java). Please review the following table to see what is applicable for your class.
ISO |
|
Java |
Handling of Text |
Example |
|---|---|---|---|---|
Enum |
0:1 |
|
A frozen set of machine readable text - Java 5 only |
|
CodeList |
0:1 |
|
Extensible set of machine readable text |
|
CharacterString |
0:1 |
|
Free form machine readable text (for things like code and version) |
|
CharacterString |
0:1 |
|
Free form human readable text |
|
Common Data Objects
ISO |
|
Java |
Common Data Objects |
|---|---|---|---|
|
0:1 |
|
Integer with sentinel values for positive and negative Infinity |
Multiplicity & Collections
ISO |
|
Java |
Comments |
|---|---|---|---|
Real |
0:N |
|
|
Integer |
0:N |
|
|
Point |
0:N |
|
Extends |
ISO |
|
Java |
Collections |
TransfiniteSet |
|
|
|
Collection |
|
|
Allow implementations to be specific |
Set |
|
|
|
Bag |
|
|
|
Sequence |
|
|
|
CircularSequence |
|
none |
We have no representation at this time |
Dictionary |
|
|
|
KeyValuePair |
|
|
|
Tips and Tricks
| Why use Objects rather then Primitives? You may of noticed that we recomend using Objects more often then primitives - the simple reason is to use null to represent an unknown value. Please note that even attributes marked as mandatory by the specification may have a null value at runtime. Either when the object is first being set up, or if a value is actually unknown. Not filling in a mandatory value would make an instance incomplete or invalid (but we still need to let it exist). There are several problems with direct use of primitive types in such case: Why are primitives so bad?
Working with partial content can be done, some specifications from the OGC are kind enough to provide "default" values in this case. Default values are often best captured as part of the javadocs for a factory interface, or even as static final values (if you are lucky enough to be working with a read-only interface). |
| Representing Data Objects In a traditional programming language we would identify these objects using structs, in Java we encode them as Data Objects. The idea here is that the object is completly defined by its fields. To define a Data Object in Java your interface needs to includes a definition of Here is an example - BEFORE: A Normal Interface To turn this into a Data Object we need to supply After: As a Data Object |
| Use of NullObjects - when null is not enough This is some of the only time you will see classes GeoAPI. When information is unknown we would like to represent it as null. However the meaning of "null" may change depending on where in a specification it is used. Consider the following example:
In these cases we can supply a NullObject (actually two of them - one for each meaning).
The javadocs will end up refering to these constants: |
Follow Java Naming Conventions
The usual Java naming convetions apply, use of CamelCase for classes methodNames and fieldNames. Use of UNDERSCORE_NAMING for ENUMERATIONS, and STATIC_FINALS (as appear in CodeLists).
Java Beans Naming Conventions for "Model" (ie Fields Access)
While mapping from UML field names please keep the java bean naming conventions in mind:
Integer getNumber()void setNumber(Integer number)Double getMeasure()void setMeasure(Integer number)Boolean isFlag()void setFlag(Boolean flag)List<Identifier> getIdentifiers()
Java Bean naming should be used for the "model" represented by the ISO specification.
Collections Naming Conventions for "Views" (ie Query or Derrived Information)
Collection naming should be used for the "pragmatic" needs born by our experience. They are used to make data access easier (in much the same way Map.keySet() and Map.values() represent alternate views on a ap).
Type Narrowing
Be specific with Type Narrowing and Javadocs
If a common set of assumptions can be named, go for it.
When we subclass we can use two tools to be more specific:
- Using type narrowing we can be more specific about the return values
- By providing javadocs (even for methods that have not changed in signature)
| Be Intentional with Collections In the above example we can only type narrow the Collection class (from Collection to List), we cannot type narrow the element class without getting deeper into Java generics. Depending on what you do with collections and generics you are in for a world of hurt - the section on interface design will cover the trade-offs involved. |