protected <TYPE extends AbstractModel> TYPE returnFetchResult(Class<TYPE> modelClass, SquidCursor<TYPE> cursor) { try { if (cursor.getCount() == 0) { return null; } TYPE toReturn = modelClass.newInstance(); toReturn.readPropertiesFromCursor(cursor); return toReturn; } catch (Exception e) { throw new RuntimeException(e); } finally { cursor.close(); } }
private static void checkCacheIntegrity(AbstractModel model, JSONProperty<?> property, String transitoryKey) { if (!model.hasTransitory(transitoryKey)) { return; } JSONObjectHolder<?> holder = getJSONTransitory(model, transitoryKey); if (model.containsValue(property) || model.getDefaultValues().containsKey(property.getName())) { String jsonValue = model.get(property); if (SqlUtils.equals(holder.jsonString, jsonValue)) { return; } } model.clearTransitory(transitoryKey); }
@Override public Void visitString(Property<String> property, T dst, ViewModel src) { Property<String> toSet = getPropertyToSet(property); if (src.containsValue(property)) { dst.set(toSet, src.get(property)); } return null; }
/** * Convenience for using transitory data as a flag. Removes the transitory data for this key if one existed. * * @param key the key for the transitory data * @return true if a transitory object is set for the given flag, false otherwise */ public boolean checkAndClearTransitory(String key) { if (hasTransitory(key)) { clearTransitory(key); return true; } return false; }
/** * Set the columns and values to update based on the specified model object * * @return this Update object, to allow chaining method calls */ public Update fromTemplate(AbstractModel template) { if (!template.isModified()) { throw new IllegalArgumentException("Template has no values set to use for update"); } ValuesStorage setValues = template.getSetValues(); for (Entry<String, Object> entry : setValues.valueSet()) { valuesToUpdate.put(entry.getKey(), entry.getValue()); } invalidateCompileCache(); return this; }
/** * Sets the given JSON-serialized property to the given value * * @return true if the value object was successfully serialized, false otherwise */ public static <T> boolean setValueAsJSON(AbstractModel model, JSONProperty<T> property, T data, Type javaType) { try { String json = null; if (data != null) { if (MAPPER == null) { throw new NullPointerException("JSONPropertySupport needs to be initialized with a " + "JSONMapper instance using setJSONMapper()"); } json = MAPPER.toJSON(data, javaType); if (model.containsNonNullValue(property) && json.equals(model.get(property))) { return false; } } model.set(property, json); putJSONTransitory(model, transitoryKeyForProperty(property), data, json); return true; } catch (Exception e) { Logger.w(TAG, "Error serializing object to JSON string: " + data, e); // TODO: Should this throw? return false; } }
/** * Deserialize a JSON string property into the specified Java type */ public static <T> T getValueFromJSON(AbstractModel model, JSONProperty<T> property, Type javaType) { String transitoryKey = transitoryKeyForProperty(property); checkCacheIntegrity(model, property, transitoryKey); if (!model.hasTransitory(transitoryKey)) { T data = null; String json = model.get(property); // Will throw if model doesn't have property if (json != null) { try { if (MAPPER == null) { throw new NullPointerException("JSONPropertySupport needs to be initialized with a " + "JSONMapper instance using setJSONMapper()"); } data = MAPPER.fromJSON(json, javaType); } catch (Exception e) { // TODO: Should this throw or at least not cache null? Logger.w(TAG, "Error deserializing JSON string: " + json, e); model.clearValue(property); } } putJSONTransitory(model, transitoryKey, data, json); return data; } JSONObjectHolder<T> holder = getJSONTransitory(model, transitoryKey); return holder.parsedObject; }
/** @return a mapping of all field/value pairs merged across data sources */ public ValuesStorage getMergedValues() { ValuesStorage mergedValues = newValuesStorage(); ValuesStorage defaultValues = getDefaultValues(); if (defaultValues != null) { mergedValues.putAll(defaultValues); } if (values != null) { mergedValues.putAll(values); } if (setValues != null) { mergedValues.putAll(setValues); } return mergedValues; }
/** * Return the value of the specified {@link Property}. The model prioritizes values as follows: * <ol> * <li>values explicitly set using {@link #set(Property, Object)} or a generated setter</li> * <li>values written to the model as a result of fetching it using a {@link SquidDatabase} or constructing it from * a {@link SquidCursor}</li> * <li>the set of default values as specified by {@link #getDefaultValues()}</li> * </ol> * If a value is not found in any of those places, the result depends on the value of the throwOnFail parameter. * If true, an exception will be throw, if false, null is returned. * * @return the value of the specified property * @throws UnsupportedOperationException if the value is not found in the model */ public <TYPE> TYPE get(Property<TYPE> property, boolean throwOnFail) { if (setValues != null && setValues.containsKey(property.getName())) { return getFromValues(property, setValues); } else if (values != null && values.containsKey(property.getName())) { return getFromValues(property, values); } else if (getDefaultValues().containsKey(property.getName())) { return getFromValues(property, getDefaultValues()); } else if (throwOnFail) { throw new UnsupportedOperationException(property.getName() + " not found in model. Make sure the value was set explicitly, read from a cursor," + " or that the model has a default value for this property."); } return null; }
/** * Copies values from the given {@link ValuesStorage} into the model. The values will be added to the model as read * values (i.e. will not be considered set values or mark the model as dirty). */ public void readPropertiesFromValuesStorage(ValuesStorage values, Property<?>... properties) { prepareToReadProperties(); if (values != null) { for (Property<?> property : properties) { if (values.containsKey(property.getName())) { this.values.put(property.getName(), getFromValues(property, values), true); } } } }
/** * Equivalent to <pre>get(property, true)</pre> * * @return the value of the specified property * @throws UnsupportedOperationException if the value is not found in the model */ public <TYPE> TYPE get(Property<TYPE> property) { return get(property, true); }
/** * Use merged values to compare two models to each other. Must be of exactly the same class. */ @Override public boolean equals(Object other) { return other != null && getClass().equals(other.getClass()) && getMergedValues() .equals(((AbstractModel) other).getMergedValues()); }
/** * Convenience for using transitory data as a flag. Removes the transitory data for this key if one existed. * * @param key the key for the transitory data * @return true if a transitory object is set for the given flag, false otherwise */ public boolean checkAndClearTransitory(String key) { if (hasTransitory(key)) { clearTransitory(key); return true; } return false; }
/** @return a mapping of all field/value pairs merged across data sources */ public ValuesStorage getMergedValues() { ValuesStorage mergedValues = newValuesStorage(); ValuesStorage defaultValues = getDefaultValues(); if (defaultValues != null) { mergedValues.putAll(defaultValues); } if (values != null) { mergedValues.putAll(values); } if (setValues != null) { mergedValues.putAll(setValues); } return mergedValues; }
/** * Return the value of the specified {@link Property}. The model prioritizes values as follows: * <ol> * <li>values explicitly set using {@link #set(Property, Object)} or a generated setter</li> * <li>values written to the model as a result of fetching it using a {@link SquidDatabase} or constructing it from * a {@link SquidCursor}</li> * <li>the set of default values as specified by {@link #getDefaultValues()}</li> * </ol> * If a value is not found in any of those places, the result depends on the value of the throwOnFail parameter. * If true, an exception will be throw, if false, null is returned. * * @return the value of the specified property * @throws UnsupportedOperationException if the value is not found in the model */ public <TYPE> TYPE get(Property<TYPE> property, boolean throwOnFail) { if (setValues != null && setValues.containsKey(property.getName())) { return getFromValues(property, setValues); } else if (values != null && values.containsKey(property.getName())) { return getFromValues(property, values); } else if (getDefaultValues().containsKey(property.getName())) { return getFromValues(property, getDefaultValues()); } else if (throwOnFail) { throw new UnsupportedOperationException(property.getName() + " not found in model. Make sure the value was set explicitly, read from a cursor," + " or that the model has a default value for this property."); } return null; }
/** * Copies values from the given {@link ValuesStorage} into the model. The values will be added to the model as read * values (i.e. will not be considered set values or mark the model as dirty). */ public void readPropertiesFromValuesStorage(ValuesStorage values, Property<?>... properties) { prepareToReadProperties(); if (values != null) { for (Property<?> property : properties) { if (values.containsKey(property.getName())) { this.values.put(property.getName(), getFromValues(property, values), true); } } } }
/** * Set the columns and values to update based on the specified model object * * @return this Update object, to allow chaining method calls */ public Update fromTemplate(AbstractModel template) { if (!template.isModified()) { throw new IllegalArgumentException("Template has no values set to use for update"); } ValuesStorage setValues = template.getSetValues(); for (Entry<String, Object> entry : setValues.valueSet()) { valuesToUpdate.put(entry.getKey(), entry.getValue()); } invalidateCompileCache(); return this; }
/** * Equivalent to <pre>get(property, true)</pre> * * @return the value of the specified property * @throws UnsupportedOperationException if the value is not found in the model */ public <TYPE> TYPE get(Property<TYPE> property) { return get(property, true); }
@Override public int hashCode() { return getMergedValues().hashCode() ^ getClass().hashCode(); }
@Override public Void visitInteger(Property<Integer> property, T dst, ViewModel src) { Property<Integer> toSet = getPropertyToSet(property); if (src.containsValue(property)) { dst.set(toSet, src.get(property)); } return null; }