package com.ligati.apipixie; import com.ligati.apipixie.exception.APIParsingException; import com.ligati.apipixie.http.APIHttpManager; import com.ligati.apipixie.tools.*; import org.apache.log4j.Logger; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import sun.reflect.generics.reflectiveObjects.NotImplementedException; import java.util.Collection; import java.util.LinkedList; import java.util.List; public class APIService<T, K> { private static final Logger logger = Logger.getLogger(APIService.class); private final APIPixie pixie; private final String entityName; private final String entityUrl; private final APIHolder<T, K> holder; private final APIHttpManager http; public APIService(APIPixie pixie, Class<T> clazz, APIHttpManager http) { this.pixie = pixie; this.holder = new APIHolder<>(clazz, pixie.isFeatureEnabled(APIPixieFeature.FAIL_ON_UNKNOWN_PROPERTIES)); this.http = http; this.entityName = clazz.getSimpleName(); this.entityUrl = this.getEntityUrl(clazz); } private String getEntityUrl(Class<T> clazz) { String annotationUrl = AnnotationUtil.getEntityUrl(clazz); if (annotationUrl != null) return annotationUrl; return UrlUtil.plural(clazz.getSimpleName().toLowerCase()); } public List<T> getAll() { logger.debug("Getting all the " + entityName); JSONArray array = this.http.getArray(this.buildUrl("")); return this.jsonArrayToEntities(array); } public T get(K id) { logger.debug("Getting the " + entityName + "#" + id); JSONObject json = this.http.getObject(this.buildUrl(id.toString())); return this.jsonObjectToEntity(json); } public T post(T entity) { logger.debug("Posting a " + entityName); JSONObject json = entityToJsonObject(entity); json = this.http.postObject(this.buildUrl(""), json); return this.jsonObjectToEntity(json); } public T put(T entity) { logger.debug("Putting a " + entityName); K id = this.holder.getId(entity); JSONObject json = entityToJsonObject(entity); json = this.http.putObject(this.buildUrl(id.toString()), json); return this.jsonObjectToEntity(json); } public void delete(T entity) { Preconditionner.checkNotNull(entity, "The given entity is null."); K id = this.holder.getId(entity); Preconditionner.checkNotNull(id, "The given entity has no id."); this.deleteById(id); } public void deleteById(K id) { logger.debug("Deleting the " + entityName + "#" + id); Preconditionner.checkNotNull(id, "The given id is null."); this.http.deleteObject(this.buildUrl(id.toString())); } private String buildUrl(String path) { StringBuilder sb = new StringBuilder(); sb.append(this.pixie.getAPIUrl()); sb.append("/"); sb.append(this.entityUrl); if (path != null && !path.isEmpty()) { sb.append("/"); sb.append(path); } return sb.toString(); } private List<T> jsonArrayToEntities(JSONArray array) { List<T> list = new LinkedList<>(); for (int i = 0; i < array.length(); i++) { try { JSONObject json = array.getJSONObject(i); T entity = this.jsonObjectToEntity(json); list.add(entity); } catch (JSONException e) { if (logger.isDebugEnabled()) e.printStackTrace(); throw new APIParsingException("An error occurred while reading the json array", e); } } return list; } protected T jsonObjectToEntity(JSONObject json) { T entity = this.holder.create(); for (String name : JSONObject.getNames(json)) { try { ComplexField complexField = this.holder.getComplexField(name); if (complexField != null) entity = this.processComplexFieldJsonToEntity(complexField, entity, json, name); else entity = this.holder.set(entity, name, json.get(name)); } catch (JSONException | InstantiationException | IllegalAccessException e) { if (logger.isDebugEnabled()) e.printStackTrace(); throw new APIParsingException("An error occurred while reading the json property: " + name, e); } } return entity; } private T processComplexFieldJsonToEntity(ComplexField complexField, T entity, JSONObject json, String propertyName) throws InstantiationException, IllegalAccessException, JSONException { switch (complexField.getType()) { case BASIC_COLLECTION: return this.processBasicCollectionJsonToEntity(complexField, entity, json, propertyName); case ENTITY_COLLECTION: return this.processEntityCollectionJsonToEntity(complexField, entity, json, propertyName); case NESTED_ENTITY: return this.processNestedEntityJsonToEntity(complexField, entity, json, propertyName); } throw new NotImplementedException(); } @SuppressWarnings("unchecked") private T processBasicCollectionJsonToEntity(ComplexField complexField, T entity, JSONObject json, String propertyName) throws InstantiationException, IllegalAccessException, JSONException { JSONArray array = json.getJSONArray(propertyName); Collection collection = complexField.getCollectionClass().newInstance(); for (int i = 0; i < array.length(); i++) collection.add(array.get(i)); return this.holder.set(entity, propertyName, collection); } @SuppressWarnings("unchecked") private T processEntityCollectionJsonToEntity(ComplexField complexField, T entity, JSONObject json, String propertyName) throws InstantiationException, IllegalAccessException, JSONException { JSONArray array = json.getJSONArray(propertyName); Collection collection = complexField.getCollectionClass().newInstance(); APIService service = new APIService(pixie, complexField.getClazz(), http); for (int i = 0; i < array.length(); i++) collection.add(service.jsonObjectToEntity(array.getJSONObject(i))); return this.holder.set(entity, propertyName, collection); } @SuppressWarnings("unchecked") private T processNestedEntityJsonToEntity(ComplexField complexField, T entity, JSONObject json, String propertyName) throws InstantiationException, IllegalAccessException, JSONException { JSONObject nestedJSON = json.getJSONObject(propertyName); APIService service = new APIService(pixie, complexField.getClazz(), http); Object nestedEntity = service.jsonObjectToEntity(nestedJSON); return this.holder.set(entity, propertyName, nestedEntity); } protected JSONObject entityToJsonObject(T entity) { JSONObject json = new JSONObject(); for (String propertyName : this.holder.getPropertiesNames()) try { ComplexField complexField = this.holder.getComplexField(propertyName); if (complexField != null) json.put(propertyName, this.processComplexFieldEntityToJson(complexField, entity, propertyName)); else json.put(propertyName, this.holder.get(entity, propertyName)); } catch (JSONException e) { if (logger.isDebugEnabled()) e.printStackTrace(); throw new APIParsingException("An error occurred while reading the entity field: " + propertyName, e); } return json; } private Object processComplexFieldEntityToJson(ComplexField complexField, T entity, String propertyName) throws JSONException { switch (complexField.getType()) { case BASIC_COLLECTION: return this.processBasicCollectionEntityToJson(complexField, entity, propertyName); case ENTITY_COLLECTION: return this.processEntityCollectionEntityToJson(complexField, entity, propertyName); case NESTED_ENTITY: return this.processNestedEntityToJson(complexField, entity, propertyName); } throw new NotImplementedException(); } private JSONArray processBasicCollectionEntityToJson(ComplexField complexField, T entity, String propertyName) { JSONArray array = new JSONArray(); Object nestedObjects = this.holder.get(entity, propertyName); if (nestedObjects == null) return null; for (Object nestedObject : complexField.getCollectionClass().cast(nestedObjects)) array.put(nestedObject); return array; } @SuppressWarnings("unchecked") private JSONArray processEntityCollectionEntityToJson(ComplexField complexField, T entity, String propertyName) { JSONArray array = new JSONArray(); Object nestedEntities = this.holder.get(entity, propertyName); if (nestedEntities == null) return null; APIService service = new APIService(pixie, complexField.getClazz(), http); for (Object nestedEntity : complexField.getCollectionClass().cast(nestedEntities)) { JSONObject nestedJson = service.entityToJsonObject(nestedEntity); array.put(nestedJson); } return array; } @SuppressWarnings("unchecked") private JSONObject processNestedEntityToJson(ComplexField complexField, T entity, String propertyName) throws JSONException { Object nestedEntity = this.holder.get(entity, propertyName); if (nestedEntity == null) return null; APIService service = new APIService(pixie, complexField.getClazz(), http); return service.entityToJsonObject(nestedEntity); } }