Skip to content

New Dynamic Model pattern to support type-checked additional properties #23

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,14 +1,103 @@
package com.ibm.cloud.sdk.core.service.model;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import com.google.gson.reflect.TypeToken;
import com.ibm.cloud.sdk.core.util.GsonSingleton;

/**
* Abstract model class for objects which may have dynamic properties attached to them,
* which is represented with an internal map.
* Abstract model class for a model which supports dynamic (arbitrary) properties of type T.
*/
public abstract class DynamicModel extends HashMap<String, Object> implements ObjectModel {
public abstract class DynamicModel<T> implements ObjectModel {
private TypeToken<T> additionalPropertyTypeToken;

// The set of dynamic properties associated with this object.
private Map<String, T> dynamicProperties = new HashMap<>();

/**
* Force use of 1-arg ctor.
*/
@SuppressWarnings("unused")
private DynamicModel() {
}

/**
* This ctor accepts a TypeToken instance that represents the type of values stored in the map.
*
* @param t
* the TypeToken which represents the type of map values
*/
public DynamicModel(TypeToken<T> t) {
this.additionalPropertyTypeToken = t;
}

/**
* Returns the TypeToken which describes the type of additional properties stored in the map.
* @return The TypeToken which describes the map value type
*/
public TypeToken<T> getAdditionalPropertyTypeToken() {
return this.additionalPropertyTypeToken;
}

/**
* Sets an arbitrary property.
*
* @param key
* the name of the property to set
* @param value
* the value of the property to be set
* @return the previous value of the property, or null if the property was not previously set
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The convention for all other setters in model classes is to return void. Is there good reason to break with that convention here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the reason I followed the "Map.put" model is so that I could easily tell if we have a duplicate property while deserializing a JSON string (by checking to see if the result of the set operation is non-null). Technically, I don't need to do it this way... i could first check to see if a value already exists for a given key, but the Map "put" method (which is really just wrapped by DynamicModel.setProperty) does that all in one step so thought it would be better to just follow that same pattern with the setProperty method.

*/
public T setProperty(String key, T value) {
return this.dynamicProperties.put(key, value);
}

/**
* Returns the value of the specified property.
*
* @param key
* the name of the property to get
* @return the value of the property, or null if the property is not set
*/
public T getProperty(String key) {
return this.dynamicProperties.get(key);
}

/**
* Returns a map containing the arbitrary properties set on this object.
*
* @return a copy of the map containing arbitrary properties set on this object
*/
public Map<String, T> getProperties() {
return new HashMap<String, T>(this.dynamicProperties);
}

/**
* Returns the names of arbitrary properties set on this object.
* @return a set containing the names of arbitrary properties set on this object
*/
public Set<String> getPropertyNames() {
return this.dynamicProperties.keySet();
}
/**
* Removes the specified property from this object's map of arbitrary properties.
*
* @param key
* the name of the property to be removed
* @return the previous value of the property, or null if the property was not previously set
*/
public T removeProperty(String key) {
return this.dynamicProperties.remove(key);
}

/**
* Removes all of the arbitrary properties set on this object.
*/
public void removeProperties() {
this.dynamicProperties.clear();
}

/*
* (non-Javadoc)
Expand All @@ -23,7 +112,7 @@ public boolean equals(Object o) {
return false;
}

final DynamicModel other = (DynamicModel) o;
final DynamicModel<?> other = (DynamicModel<?>) o;

return toString().equals(other.toString());
}
Expand Down
Loading