Next: Handling Arrays: The Horizon Up: The Horizon Metadata Model Previous: Handle Updateable Metadata Values

## Protecting Metadata Via Defaults

The `Metadata` class shares another important feature of the `Properties` class in its the support for default values; that is, `Metadata` objects can store two sets of data within themselves: primary data and default data. Calls to `getMetadatum(String)` will return requested data from the primary set; if it does not exist there, the data in the default list is searched for a value. (One can prevent the search of the default set by calling `getMetadatum(String, null)`.)

The default data are stored as a protected `Metadata`
[1] field within the primary `Metadata` class and can only be set during construction. The `Metadata` class interface never gives out a reference to the defaults list, preventing client objects from adding, removing, or replacing any of its contents. Many Horizon classes exploit this feature as a way of keeping exclusive control of the metadata that they manage but still allow read-only access to the metadata. For example, the `CoordinateSystem` class can share its metadata with other objects via its `getMetadata` method; however, it probably does not want to allow other objects to alter the metadata list. So, it has two choices: it can either clone the `Metadata` list and return the new copy:

    protected Metadata mdata;
    ...
    public Metadata getMetadata() {
        return mdata.clone();
    }

or it can use a ``protection'' technique by creating a new `Metadata` list with `CoordinateSystem`'s own `Metadata` as the default:

    protected Metadata mdata;
    ...
    public Metadata getMetadata() {
        return new Metadata(mdata);
    }

Why wouldn't the `CoordinateSystem` object want to return a clone? That depends in part on how `clone` is implemented. The deepest possible `clone` implementation would clone both the primary and default hashtables plus each object in the hashtables. This could potentially be an expensive operation if the tables contain a lot of data, and it could be inefficient if much of the data is of a non-updatable type (e.g. `Double`). A less deep clone would clone only the primary hashtable and the default `Metadata` list; however, this also could get expensive if the default list has a default list of its own which also has a default list and so on. By contrast, the ``protection'' implementation for `getMetadata` does not clone any hashtables; the biggest overhead incurred is in the creation of a single empty hashtable.

In actuality, the `Metadata`'s `clone` method only clones the primary hastable which may or may not be an expensive operation. Nevertheless, there are some advantages the ``protection'' technique provides to client objects. Suppose object `aClient` calls a `CoordinateSystem`'s `getMetadata` method which returns a `Metadata` object using the ``protection'' technique. `aClient` is free to update its `Metadata` list without affecting the copy help the `CoordinateSystem`; replacement operations really only hide the original values. Thus, `aClient` can return to the default value later, if necessary, by removing any data its added. If, by contrast, `aClient` had gotten a clone, updates would have removed the original values of the primary hashtable. Another feature relates to the fact that the `CoordinateSystem` still has a reference to the list being used as the defaults of `aClient`'s list; in this case, we say that `aClient`'s `Metadata` object is ``attached'' to the `CoordinateSystem`. The consequence of this is that whenever `aClient` calls the `getMetadatum` method, it is guaranteed to get the most up-to-date value for the metadatum in use by the `CoordinateSystem`.

Client objects may not want their `Metadata` objects to have such close connections with the `Metadata`'s originator. Therefore, the `Metadata` class provides a variety of methods offering different levels of cloning and ``attachment'':

`
- void setDefaults(Metadata)
- Metadata getDefaults()
- void detach()
- Object clone()
- Metadata detachedClone()
- Metadata cloneDefaults()
- Metadata deepClone()
`

For instance, `aClient` could remove the defaults list in its `Metadata` object by calling `setDefaults(null)`. A less severe and usually more useful alternative would be to call `detach()` which replaces the defaults list with a clone. As a result, any changes the `CoordinateSystem` makes to its metadata list will not affect the list held by `aClient`.

Of course, protecting metadata in a defaults list does not protect against updates to data internal to the objects contained within the list. Following our above example, suppose `aClient` gets a `double[]` array from the `Metadata` object it got from `CoordinateSystem`. It can now change the value of one of the elements of that array. The next time the `CoordinateSystem` accesses the array, it would see the update. The moral here is that if you want client objects to only have complete, deep read-only access to a `Metadata` list, do not use updateable objects as values in that list. The exceptions to this rule, however, are `Metavector` objects and other `Metadata` objects.

The `Metadata` class takes special care when a metadatum value is of type `Metadata`. If the `getMetadatum` method must resort to getting a value from the default list and if that value is itself of type `Metadata`, then it will not return a direct reference to the sub-`Metadata`; instead it will set it as the default list in a new `Metadata` instance which is then returned, thus protecting it from updates by the client.

Arrays are often too useful to avoid using to hold metadata; therefore, the Horizon package also provides the `Metavector` class (described in §2.4) for this purpose. A `Metavector` is much like a `Metadata` object, including its support for default data; the difference is that instead of using a hashtable for storing its data, it uses an array. The `Metadata` class will also watch out for `Metavector` objects extracted from the defaults list, protecting them when necessary.

In summary, the defaults Metadata list can provide a read-only access to metadata, limited by whether or not the stored values are of an updateable type. Full read-only access can be achieved if the only updateable classes used are `Metadata` and `Metavector`. This should be sufficient for most cases, because the `Metadata` class can be considered a kind of structure (as in C programming)--a class with only fields but no methods. In place of the methods, a metadata list is governed by the schema definition for handling the data. Of course, a `Metadata` object is not as powerful as a full-featured class; however, one of the main purposes of the `Metadata` class is to provide a container for transporting this information, particularly from persistant storage like a data file of a specific format. Once carried to the part of the code that will process the data, the data can be converted into a fully intelligent class. In fact, this is what is done with the Horizon coordinate classes, the topic of section §3.

Next: Handling Arrays: The Horizon Up: The Horizon Metadata Model Previous: Handle Updateable Metadata Values