package org.activityinfo.model.resource; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.Maps; import org.activityinfo.model.type.FieldValue; import org.activityinfo.model.type.primitive.BooleanFieldValue; import org.activityinfo.model.type.primitive.TextValue; import javax.annotation.Nonnull; import javax.validation.constraints.NotNull; import java.util.Collections; import java.util.List; import java.util.Map; /** * Base class for Resource and Record */ public class PropertyBag<T extends PropertyBag> { private final Map<String, Object> properties = Maps.newHashMap(); public PropertyBag<T> copy() { final PropertyBag<T> copy = new PropertyBag<>(); copy.properties.putAll(this.properties); return copy; } /** * * @return the value of the given property, or {@code null} if there * is no value for this property. */ public Object get(String propertyName) { return properties.get(propertyName); } /** * @return true if there is a value for the given property */ public boolean has(String propertyName) { return properties.containsKey(propertyName); } /** * @return the value of the given boolean property * @throws java.lang.NullPointerException */ public boolean getBoolean(String propertyName) { return (Boolean) getNonNullPropertyValue(propertyName); } public Boolean isBoolean(String propertyName) { Object value = properties.get(propertyName); if(value == Boolean.TRUE) { return true; } else if(value == Boolean.FALSE) { return false; } else { return null; } } /** * @return the value of the given boolean property, or the {@code defaultValue} * @throws java.lang.ClassCastException if the value of the property is not a boolean * @throws java.lang.NullPointerException if there is no value for the property */ public boolean getBoolean(String propertyName, boolean defaultValue) { Object value = properties.get(propertyName); if(value == Boolean.TRUE) { return true; } else if(value == Boolean.FALSE) { return false; } else { return defaultValue; } } /** * * @return the field's value as an immutable list of {@code Record}s. * If the field has no value, or the value is not of the array type, * an empty list is returned. */ @Nonnull public List<Record> getRecordList(String propertyName) { Object value = properties.get(propertyName); if(value instanceof List) { return (List)value; } return Collections.emptyList(); } @Nonnull public List<String> getStringList(String propertyName) { Object value = properties.get(propertyName); if(value instanceof List) { return (List)value; } return Collections.emptyList(); } /** * @return the value of the given property as a String * @throws java.lang.ClassCastException if the value of the property is not a string * @throws java.lang.NullPointerException if there is no value for the property */ @Nonnull public String getString(String propertyName) { return (String) getNonNullPropertyValue(propertyName); } public String isString(String propertyName) { Object value = properties.get(propertyName); if(value instanceof String) { return (String)value; } return null; } /** * @return this field's value as a {@code Record} * */ @Nonnull public Record getRecord(String propertyName) { return (Record) getNonNullPropertyValue(propertyName); } public Record isRecord(String propertyName) { Object value = properties.get(propertyName); if(value instanceof Record) { return (Record) value; } return null; } /** * @return this field's value as a {@code Resource} * */ @Nonnull public Resource getResource(String propertyName) { Resource value = (Resource) properties.get(propertyName); if(value == null) { throw new NullPointerException(propertyName); } return value; } @Nonnull public ResourceId getResourceId(String propertyName) { String value = (String) properties.get(propertyName); if(value == null) { throw new NullPointerException(propertyName); } return ResourceId.valueOf(value); } public ResourceId isResourceId(String propertyName) { Object value = properties.get(propertyName); if(value instanceof ResourceId) { return (ResourceId) value; } return null; } /** * @return the value of this field as a {@code double} * @throws java.lang.ClassCastException if the value of the property is not a Number * @throws java.lang.NullPointerException if there is no value for the property */ public double getDouble(String propertyName) { Number value = (Number) getNonNullPropertyValue(propertyName); return value.doubleValue(); } /** * @return the value of this field as a {@code integer} * @throws java.lang.ClassCastException if the value of the property is not a Number * @throws java.lang.NullPointerException if there is no value for the property */ public int getInt(String propertyName) { Number value = (Number) getNonNullPropertyValue(propertyName); return value.intValue(); } /** * Sets the named property to the given {@code record}, or * removes the property if {@code record} is {@code null}. */ public T set(String propertyName, Record record) { if(record == null) { properties.remove(propertyName); } else { properties.put(propertyName, record); } return (T)this; } public PropertyBag<T> set(@NotNull ResourceId fieldId, FieldValue fieldValue) { Preconditions.checkNotNull(fieldId); if (fieldValue == null) { remove(fieldId.asString()); } else if (fieldValue instanceof TextValue) { set(fieldId.asString(), ((TextValue) fieldValue).toString()); } else if (fieldValue instanceof BooleanFieldValue) { set(fieldId.asString(), fieldValue == BooleanFieldValue.TRUE); } else if(fieldValue instanceof IsRecord) { set(fieldId.asString(), ((IsRecord) fieldValue).asRecord()); } else { throw new UnsupportedOperationException(fieldId + " = " + fieldValue); } return this; } public void remove(String fieldName) { properties.remove(fieldName); } /** * Sets the named property to the given {@code string}, or * removes the property if {@code string} is {@code null} or * empty. */ public T set(String propertyName, String string) { if (Strings.isNullOrEmpty(string)) { properties.remove(propertyName); } else { properties.put(propertyName, string); } return (T)this; } /** * Sets the named property to the given {@code recordList}, or * removes the property if {@code recordList} is {@code null} or * empty. */ public T set(String propertyName, List<Record> recordList) { if (recordList == null || recordList.isEmpty()) { properties.remove(propertyName); } else { properties.put(propertyName, recordList); } return (T)this; } /** * Sets the named property to the given {@code doubleValue} */ public T set(String propertyName, double doubleValue) { properties.put(propertyName, doubleValue); return (T)this; } /** * Sets the named property to the name of the given * {@code enumValue}, or removes the property if * {@code enumValue} is {@code null}. * * Equivalent to calling: * <blockquote> * {@code set(propertyName, enumValue == null ? null : enumValue.name()); } * </blockquote> */ public T set(String propertyName, Enum<?> enumValue) { if(enumValue == null) { properties.remove(propertyName); } else { properties.put(propertyName, enumValue.name()); } return (T)this; } public void clear() { properties.clear(); } private Object getNonNullPropertyValue(String propertyName) { Object value = properties.get(propertyName); if(value == null) { throw new NullPointerException(propertyName); } return value; } /** * Sets the named property to the given value, * or removes the property if the {@code value} is null. * * @throws java.lang.IllegalArgumentException if {@code value} is not an instance of * <ul> * <li>{@code java.lang.String}</li> * <li>{@code java.lang.Number}</li> * <li>{@code java.lang.Boolean}</li> * <li>{@code Record}</li> * <li>{@code java.util.List}</li> * </ul> */ public T set(String propertyName, Object value) { if(value == null) { properties.remove(propertyName); } else if(value instanceof ResourceId) { properties.put(propertyName, ((ResourceId) value).asString()); } else { assert validPropertyValue(value) : "Invalid " + propertyName + " = " + value + " (" + value.getClass().getName() + ")"; properties.put(propertyName, value); } return (T)this; } private boolean validPropertyValue(Object value) { if(value instanceof List) { for(Object element : (List)value) { if(!validPropertyValue(element)) { return false; } } return true; } else { return value instanceof String || value instanceof Number || value instanceof Record || value instanceof Boolean; } } /** * Sets the named property to the name of the given * {@code booleanValue} */ public T set(String propertyName, boolean booleanValue) { properties.put(propertyName, booleanValue); return (T)this; } public void setAll(PropertyBag propertyBag) { properties.putAll(propertyBag.properties); } public Map<String, Object> getProperties() { return properties; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } PropertyBag that = (PropertyBag) o; return this.getProperties().equals(that.properties); } @Override public int hashCode() { return properties.hashCode(); } @Override public String toString() { return properties.toString(); } }