All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
xapi.model.impl.AbstractModel Maven / Gradle / Ivy
Go to download
This module exists solely to package all other gwt modules into a single
uber jar. This makes deploying to non-mavenized targets much easier.
Of course, you would be wise to inherit your dependencies individually;
the uber jar is intended for projects like collide,
which have complex configuration, and adding many jars would be a pain.
package xapi.model.impl;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Map.Entry;
import java.util.Objects;
import xapi.annotation.inject.InstanceDefault;
import xapi.collect.X_Collect;
import xapi.collect.api.StringTo;
import xapi.log.X_Log;
import xapi.model.X_Model;
import xapi.model.api.Model;
import xapi.model.api.ModelKey;
import xapi.model.api.NestedModel;
import xapi.model.api.PersistentModel;
import xapi.util.api.ErrorHandler;
import xapi.util.api.SuccessHandler;
@InstanceDefault(implFor=Model.class)
public class AbstractModel implements Model, PersistentModel, NestedModel{
private static StringTo defaultValues = X_Collect.newStringMap(Object.class);
protected StringTo map;
protected Model parent;
protected ModelKey key;
public AbstractModel() {
this.map = newStringMap();
}
public AbstractModel(final String type) {
this.map = newStringMap();
setKey(X_Model.newKey(type));
}
protected StringTo newStringMap() {
return X_Collect.newStringMap(Object.class);
}
@Override
public ModelKey getKey(){
return key;
}
@Override
@SuppressWarnings("unchecked")
public T getProperty(final String key) {
final Object val = map.get(key);
if (val == null) {
final Class> type = getPropertyType(key);
return (T) getPrimitiveValue(type);
}
return (T) val;
}
@Override
@SuppressWarnings("unchecked")
public T getProperty(final String key, final T dflt) {
Object val = map.get(key);
if (val == null) {
val = dflt;
}
if (val == null) {
final Class> type = getPropertyType(key);
return (T) getPrimitiveValue(type);
} else {
return (T) val;
}
}
@Override
public Iterable> getProperties() {
return map.entries();
}
@Override
public Model setProperty(final String key, final Object value) {
try {
map.put(key, value);
} catch (final Throwable e) {
X_Log.error(e);
}
return this;
}
protected AbstractModel createNew() {
return new AbstractModel();
}
@Override
public Model removeProperty(final String key) {
map.remove(key);
return this;
}
@Override
public Model child(final String propName) {
Object existing = map.get(propName);
assert existing == null || existing instanceof Model :
"You requested a child model with property name "+propName+", but an object of type "
+existing.getClass()+" already existed in this location: "+existing;
if (existing == null){
final AbstractModel child = createNew();//ensures subclasses can control child classes.
child.parent = this;
existing = child;
map.put(propName, existing);
}
return (Model) existing;
}
@Override
public Model parent() {
return parent;
}
@Override
public Model cache(final SuccessHandler callback) {
X_Model.cache().cacheModel(this,callback);
return this;
}
@Override
public Model persist(final SuccessHandler callback) {
X_Model.persist(this, callback);
return this;
}
@Override
public Model delete(final SuccessHandler callback) {
X_Model.cache().deleteModel(this,callback);
return this;
}
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public Model load(final SuccessHandler callback, final boolean useCache) {
try{
final Model model = X_Model.cache().getModel(getKey().toString());
callback.onSuccess(model);
}catch(final Exception e){
if (callback instanceof ErrorHandler) {
((ErrorHandler) callback).onError(e);
}
}
return this;
}
@Override
public Model flush() {
return this;
}
@Override
public void clear() {
map.clear();
}
@Override
public Class> getPropertyType(final String key) {
throw new UnsupportedOperationException("Type "+getClass()+" does not support .getPropertyType()");
}
@Override
public String[] getPropertyNames() {
throw new UnsupportedOperationException("Type "+getClass()+" does not support .getPropertyNames()");
}
@Override
public boolean equals(final Object obj) {
return equalsForModel(this, obj);
}
public static boolean equalsForModel(final Model self, final Object obj) {
if (obj instanceof Model) {
final Model theirModel = (Model) obj;
final ModelKey theirKey = theirModel.getKey();
if (theirKey != null && self.getKey() != null) {
// When both models have keys, use them for equality
return Objects.equals(self.getKey(), theirKey);
}
// Otherwise, equality will do a deep check of properties.
// It is thus recommended that if you use models as keys in a map,
// that ALL models always have a key.
final String[] myProps = self.getPropertyNames();
if (!Arrays.equals(theirModel.getPropertyNames(), myProps)) {
return false;
}
for(final String key : myProps) {
if (!Objects.deepEquals(self.getProperty(key), theirModel.getProperty(key))) {
return false;
}
}
return true;
}
return false;
}
@Override
public int hashCode() {
return hashCodeForModel(this);
}
public static int hashCodeForModel(final Model m) {
final ModelKey k = m.getKey();
if (k == null) {
// With a null key, the best we can do is a quick hash based on our properties
int hash = 27;
for (final String prop : m.getPropertyNames()) {
final Object value = m.getProperty(prop);
if (value == null) {
hash += prop.hashCode();
} else {
final Class> type = m.getPropertyType(prop);
if (type.isArray()) {
// Close enough, we don't want to bother with deep hashing
hash += Array.getLength(value);
} else {
hash += prop.hashCode();
}
}
}
return hash;
}
// If there is a key, always prefer using it for the hashcode.
return k.hashCode();
}
@Override
public String toString() {
return toStringForModel(this);
}
public static String toStringForModel(final Model m) {
final StringBuilder b = new StringBuilder(m.getType()+"(\n");
if (m.getKey() != null) {
b.append("<").append(m.getKey()).append(">: ");
}
for (final String name : m.getPropertyNames()) {
final Object value = m.getProperty(name);
b.append(name).append(": ");
if (value != null && value.getClass().isArray()) {
arrayToString(b, value);
} else {
b.append(value);
}
b.append('\t');
}
return b.append("\n)").toString();
}
/**
* @param b
* @param value
* @return
*/
private static void arrayToString(final StringBuilder b, final Object value) {
b.append("[");
for (int i = 0, m = Array.getLength(value); i < m; i++) {
final Object child = Array.get(value, i);
if (child != null && child.getClass().isArray()) {
arrayToString(b, child);
} else {
b.append(child);
}
if (i < m - 1) {
b.append(", ");
}
}
b.append("]");
}
public static Object getPrimitiveValue(final Class> type) {
if (defaultValues.isEmpty()) {
defaultValues.put(boolean.class.getName(), false);
defaultValues.put(byte.class.getName(), (byte)0);
defaultValues.put(short.class.getName(), (short)0);
defaultValues.put(char.class.getName(), '\0');
defaultValues.put(int.class.getName(), 0);
defaultValues.put(long.class.getName(), 0L);
defaultValues.put(float.class.getName(), 0f);
defaultValues.put(double.class.getName(), 0.);
}
return defaultValues.get(type.getName());
}
@Override
public String getType() {
if (getKey() == null) {
throw new UnsupportedOperationException("Generic model "+getClass()+" does not have a key set. Cannot determine type.");
}
return getKey().getKind();
}
/**
* @see xapi.model.api.Model#setKey(xapi.model.api.ModelKey)
*/
@Override
public Model setKey(final ModelKey key) {
this.key = key;
return this;
}
}