/** * */ package xapi.test.model; import java.lang.reflect.Array; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map.Entry; import java.util.Objects; import xapi.annotation.inject.SingletonOverride; import xapi.log.X_Log; import xapi.model.api.Model; import xapi.model.api.ModelKey; import xapi.model.api.ModelQuery; import xapi.model.api.ModelQuery.QueryParameter; import xapi.model.api.ModelQueryResult; import xapi.model.content.ModelComment; import xapi.model.content.ModelContent; import xapi.model.content.ModelRating; import xapi.model.content.ModelText; import xapi.model.impl.AbstractModelService; import xapi.model.service.ModelService; import xapi.model.user.ModelUser; import xapi.test.model.content.ModelCommentTest; import xapi.test.model.content.ModelContentTest; import xapi.test.model.content.ModelRatingTest; import xapi.test.model.content.ModelTextTest; import xapi.test.model.content.ModelUserTest; import xapi.util.api.SuccessHandler; /** * @author James X. Nelson (james@wetheinter.net, @james) * */ @SingletonOverride(priority=Integer.MIN_VALUE, implFor=ModelService.class) public class ModelServiceTestImpl extends AbstractModelService { private final HashMap<ModelKey, Model> ramCache = new HashMap<>(); @Override protected <M extends Model> void doPersist(final String type, final M model, final SuccessHandler<M> callback) { X_Log.info(getClass(), "Persist",type,"as\n",model); ModelKey key = model.getKey(); if (key == null) { key = newKey(null, type); model.setKey(key); } ramCache.put(key, model); callback.onSuccess(model); } @Override @SuppressWarnings("unchecked") public <M extends Model> void load(final Class<M> modelClass, final ModelKey modelKey, final SuccessHandler<M> callback) { callback.onSuccess((M) ramCache.get(modelKey)); } /** * @see xapi.model.service.ModelService#query(java.lang.Class, xapi.model.api.ModelQuery, xapi.util.api.SuccessHandler) */ @Override public <M extends Model> void query(final Class<M> modelClass, final ModelQuery<M> query, final SuccessHandler<ModelQueryResult<M>> callback) { // The ramCache version of the service is going to be quite poor. final ModelQueryResult<M> result = new ModelQueryResult<>(modelClass); final ModelKey cursorKey = getCursorKey(query); for (final Entry<ModelKey, Model> entry : ramCache.entrySet()) { final ModelKey key = entry.getKey(); if (query.getNamespace().equals(key.getNamespace()) && modelClass == typeNameToClass.get(key.getKind())) { if (doesMatch(modelClass, query, cursorKey, entry.getValue())) { result.addModel((M) entry.getValue()); if (result.getSize() == query.getPageSize()) { break; } } } } if (result.getSize() > 0) { final List<M> list = result.getModelList(); result.setCursor(keyToString(list.get(list.size()-1).getKey())); } else { result.setCursor(query.getCursor()); } callback.onSuccess(result); } @Override public void query(final ModelQuery<Model> query, final SuccessHandler<ModelQueryResult<Model>> callback) { // The ramCache version of the service is going to be quite poor. final ModelQueryResult<Model> result = new ModelQueryResult<>(Model.class); final ModelKey cursorKey = getCursorKey(query); for (final Entry<ModelKey, Model> entry : ramCache.entrySet()) { final ModelKey key = entry.getKey(); if (query.getNamespace().equals(key.getNamespace())) { if (doesMatch(null, query, cursorKey, entry.getValue())) { result.addModel(entry.getValue()); if (result.getSize() == query.getPageSize()) { break; } } } } if (result.getSize() > 0) { final List<Model> list = result.getModelList(); result.setCursor(keyToString(list.get(list.size()-1).getKey())); } else { result.setCursor(query.getCursor()); } callback.onSuccess(result); } protected ModelKey getCursorKey(final ModelQuery<? extends Model> query) { if (query.getCursor() == null) { return null; } else { return keyFromString(query.getCursor()); } } protected boolean doesMatch(final Class<? extends Model> modelClass, final ModelQuery<? extends Model> query, final ModelKey cursorKey, final Model model) { final ModelKey key = model.getKey(); if (!query.getNamespace().equals(key.getNamespace())) { return false; } if (modelClass != null && !modelClass.isAssignableFrom(model.getClass())) { return false; } if (cursorKey != null) { // Make sure the model's key is greater than our cursor if (key.getId().compareTo(cursorKey.getId()) < 1) { return false; } } for (final QueryParameter param : query.getParameters()) { if (!matches(param, model)) { return false; } } return true; } /** * @param param * @param model * @return */ private boolean matches(final QueryParameter param, final Model model) { final Object value = model.getProperty(param.getParameterName()); assert typesEqual(value, param.getFilterValue()); switch (param.getFilterType()) { case EQUALS: return Objects.deepEquals(value, param.getFilterValue()); case CONTAINS: if (value == null) { return param.getFilterValue() == null; } if (value instanceof String) { return ((String)value).contains(String.valueOf(param.getFilterValue())); } // else do array / map / container matching... if (value.getClass().isArray()) { if (param.getFilterValue() instanceof Comparable) { return Arrays.binarySearch((Object[])value, param.getFilterValue()) != -1; } for (int i = 0, m = Array.getLength(value); i < m; i++) { if (Objects.equals(Array.get(value, i), param.getFilterValue())) { return true; } } } // We can do lists/maps later... throw new UnsupportedOperationException("Contains queries not yet supported for "+value.getClass()+" types."); case GREATER_THAN: if (value == null) { return false; } return ((Comparable)value).compareTo(param.getFilterValue()) > 0; case LESS_THAN: if (value == null) { return false; } return ((Comparable)value).compareTo(param.getFilterValue()) < 0; } return false; } private boolean typesEqual(final Object value1, final Object value2) { if (value1 == null || value2 == null) { return true; } if (value1.getClass().isAssignableFrom(value2.getClass())) { return true; } if (value2.getClass().isAssignableFrom(value1.getClass())) { return true; } return false; } @Override @SuppressWarnings("unchecked") public <T extends Model> T create(final Class<T> key) { if (key == ModelText.class) { return (T) new ModelTextTest(); } if (key == ModelContent.class) { return (T) new ModelContentTest(); } if (key == ModelRating.class) { return (T) new ModelRatingTest(); } if (key == ModelUser.class) { return (T) new ModelUserTest(); } if (key == ModelComment.class) { return (T) new ModelCommentTest(); } return super.create(key); } @Override public <M extends Model> Class<M> typeToClass(final String kind) { throw new UnsupportedOperationException("Test Model Service does not support .typeToClass()"); } }