public class TestHelper {

    /**
     * Retrive the json inside the test file
     * 
     * @param fileName
     *            the file name
     * @return string with json to test
     */
    public static String retriveJsonFile(String fileName) {

        String json = null;

        try {
            json = IOUtils.toString(TestHelper.class.getClassLoader().getResourceAsStream(fileName), "UTF-8");
        } catch (IOException e) {
            e.printStackTrace();
        }

        return json;
    }

} public class EasyJsonApiDeserializer extends EasyJsonApiMachine implements JsonDeserializer<JsonApi> {

    @Override
    public JsonApi deserialize(JsonElement jsonElem, Type jsonType, JsonDeserializationContext jsonContext) {

        JsonApi request = new JsonApi();

        if (!jsonElem.isJsonNull()) {

            boolean existJsonFormat = jsonElem.getAsJsonObject().isJsonNull();
            if (!existJsonFormat) {
                if (Assert.notNull(jsonElem.getAsJsonObject().get("data"))) {
                    request = deserializerData(jsonElem, jsonContext);
                } else if (Assert.notNull(jsonElem.getAsJsonObject().get("errors"))) {
                    request = deserializerError(jsonElem, jsonContext);
                }
            }
        }

        return request;
    }

    /**
     * Deserializer when occur an success
     * 
     * @param jsonElem
     *            the json element
     * @param jsonContext
     *            the json context
     * @return the json api object with values created
     */
    private JsonApi deserializerData(JsonElement jsonElem, JsonDeserializationContext jsonContext) {

        JsonApi request = new JsonApi();

        // FIXME: Find solution for this validation
        if (this.tokenTypesToUse.containsKey(EasyJsonApiTypeToken.TOKEN_ATTR) || this.tokenTypesToUse.containsKey(EasyJsonApiTypeToken.TOKEN_META)
                || this.tokenTypesToUse.containsKey(EasyJsonApiTypeToken.TOKEN_DEFAULT)
                || this.tokenTypesToUse.containsKey(EasyJsonApiTypeToken.TOKEN_META_RELATIONSHIP)) {

            // Parse the attribute data
            JsonArray jsonArrayData = jsonElem.getAsJsonObject().get("data").getAsJsonArray();

            // Iterate the data list
            for (int index = 0; index < jsonArrayData.size(); index++) {

                JsonObject jsonData = jsonArrayData.get(index).getAsJsonObject();

                Relationships dataRels = Nullable.RELATIONSHIPS;
                Object dataAttr = Nullable.OBJECT;

                String dataId = JsonTools.getStringInsideJson("id", jsonData);
                String dataType = JsonTools.getStringInsideJson("type", jsonData);

                // Get the attributes json
                if (Assert.notNull(jsonData.get("attributes"))) {
                    dataAttr = deserializerDataAttributes(jsonData, jsonContext);
                }

                // Get the relationship json
                if (Assert.notNull(jsonData.get("relationships"))) {
                    dataRels = deserializerDataRelationship(jsonData, jsonContext);
                }

                Data jsonApiData = new Data(dataId, dataType, dataAttr, dataRels);
                request.addData(jsonApiData);
            }
        }

        return request;
    }

    /**
     * Deserializer attributes json object
     * 
     * @param jsonAttributes
     *            the attributes json object
     * @param jsonContext
     *            the json context
     * @return one instance of {@link Object}
     */
    private Object deserializerDataAttributes(JsonObject json, JsonDeserializationContext jsonContext) {

        return deserializerObject("attributes", json, EasyJsonApiTypeToken.TOKEN_ATTR, jsonContext);

    }

    /**
     * Deserializer relationships json object
     * 
     * @param jsonRelationships
     *            the relationship json object
     * @param jsonContext
     *            the json context
     * @return one instance of {@link Relationships}
     */
    private Relationships deserializerDataRelationship(JsonObject json, JsonDeserializationContext jsonContext) {

        Relationships relationships = new Relationships();
        Link link = Nullable.LINK;
        LinkRelated linkRelated = Nullable.LINK_RELATED;
        Object metaRelated = Nullable.OBJECT;

        JsonObject jsonRels = json.get("relationships").getAsJsonObject();

        for (Entry<String, JsonElement> jsonRelationship : jsonRels.entrySet()) {

            Relationship relationship = Nullable.RELATIONSHIP;

            JsonObject jsonRelationshipValue = jsonRelationship.getValue().getAsJsonObject();

            String jsonRelationshipKey = Assert.isNull(jsonRelationship.getKey()) ? null : jsonRelationship.getKey();

            if (Assert.notNull(jsonRelationshipValue)) {

                if (Assert.notEmpty(jsonRelationshipKey) && jsonRelationshipKey.toLowerCase().equals("meta")) {

                    metaRelated = deserializerObject("meta", jsonRelationshipValue, EasyJsonApiTypeToken.TOKEN_META, jsonContext);

                }

                JsonObject relsLinks = Assert.isNull(jsonRelationshipValue.get("links")) ? null
                        : jsonRelationshipValue.get("links").getAsJsonObject();
                JsonArray relsData = Assert.isNull(jsonRelationshipValue.get("data")) ? null : jsonRelationshipValue.get("data").getAsJsonArray();

                if (Assert.notNull(relsLinks)) {

                    String self = JsonTools.getStringInsideJson("self", relsLinks);
                    JsonObject related = Assert.isNull(relsLinks.get("related")) ? null : relsLinks.get("related").getAsJsonObject();

                    if (Assert.notNull(related)) {

                        String href = JsonTools.getStringInsideJson("href", related);

                        Object metaLinkRelated = deserializerObject("meta", related, EasyJsonApiTypeToken.TOKEN_META_RELATIONSHIP, jsonContext);

                        linkRelated = new LinkRelated(href, metaLinkRelated);

                    }

                    link = new Link(linkRelated, self);
                }

                relationship = new Relationship(jsonRelationshipKey, link, metaRelated);

                if (Assert.notNull(relsData) && Assert.notNull(relationships)) {

                    for (JsonElement data : relsData) {
                        JsonObject dataRels = data.getAsJsonObject();

                        String id = JsonTools.getStringInsideJson("id", dataRels);
                        String type = JsonTools.getStringInsideJson("type", dataRels);

                        relationship.addDataLinkage(new DataLinkage(id, type));
                    }
                }

                relationships.getRelationships().add(relationship);
            }
        }

        return relationships;
    }

    /**
     * Deserializer when occur an error
     * 
     * @param jsonElem
     *            the json element
     * @param jsonContext
     *            the json context
     * @return the json api object with values created
     */
    private JsonApi deserializerError(JsonElement jsonElem, JsonDeserializationContext jsonContext) {

        JsonApi request = new JsonApi();

        JsonArray jsonArrayErrors = jsonElem.getAsJsonObject().get("errors").getAsJsonArray();

        // Iterate the errors list
        for (int index = 0; index < jsonArrayErrors.size(); index++) {

            JsonObject jsonError = jsonArrayErrors.get(index).getAsJsonObject();

            String jsonApiErrorDetail = JsonTools.getStringInsideJson("detail", jsonError);
            String jsonApiErrorCode = JsonTools.getStringInsideJson("code", jsonError);
            String jsonApiErrorTitle = JsonTools.getStringInsideJson("title", jsonError);
            String jsonApiErrorId = JsonTools.getStringInsideJson("id", jsonError);

            Source jsonApiErrorSource = null;
            HttpStatus jsonApiErrorStatus = null;

            // Get the source json
            if (Assert.notNull(jsonError.get("source"))) {
                JsonObject jsonErrorSource = jsonError.get("source").getAsJsonObject();
                jsonApiErrorSource = jsonContext.deserialize(jsonErrorSource, Source.class);
            }

            // Get the http status json
            if (Assert.notNull(jsonError.get("status"))) {
                JsonObject jsonErrorStatus = jsonError.get("status").getAsJsonObject();
                jsonApiErrorStatus = HttpStatus.getStatus(Integer.valueOf(jsonErrorStatus.getAsString()));
            }

            Error jsonApiError = new Error(jsonApiErrorId, jsonApiErrorTitle, jsonApiErrorStatus, jsonApiErrorCode, jsonApiErrorDetail,
                    Nullable.OBJECT, jsonApiErrorSource);

            request.addError(jsonApiError);
        }

        return request;
    }

    /**
     * Deserializer object json for {@link EasyJsonApiTypeToken} sent to method
     * 
     * @param name
     *            the name of json element need extract
     * @param json
     *            the json object
     * @param typeToken
     *            the type token to convert json
     * @param jsonContext
     *            the json context
     * @return one instance of typeToken sent
     */
    private Object deserializerObject(String name, JsonObject json, EasyJsonApiTypeToken typeToken, JsonDeserializationContext jsonContext) {

        Object objDeserialized = Nullable.OBJECT;

        if (Assert.notNull(json.get(name))) {
            JsonObject jsonObject = json.get(name).getAsJsonObject();

            Type type = Assert.notNull(this.tokenTypesToUse.get(EasyJsonApiTypeToken.TOKEN_DEFAULT))
                    ? this.tokenTypesToUse.get(EasyJsonApiTypeToken.TOKEN_DEFAULT) : this.tokenTypesToUse.get(typeToken);

            if (Assert.isNull(type)) {
                throw new EasyJsonApiCastException("Doesn't find token for " + name + " resource object inside json!");
            }

            objDeserialized = jsonContext.deserialize(jsonObject, type);
        }

        return objDeserialized;

    }

} public class EasyJsonApiConfig {

    private Map<EasyJsonApiTypeToken, List<Class<?>>> classesParsed = new HashMap<>();

    private String[] packagesSearched;

    /**
     * The default constructor
     */
    public EasyJsonApiConfig() {}

    /**
     * The constructor with packages attributes
     * 
     * @param packages
     *            the packages needs to be search and parsing
     * @throws EasyJsonApiInvalidPackageException
     */
    public EasyJsonApiConfig(String... packages) throws EasyJsonApiInvalidPackageException {
        setPackagesToSearch(packages);
    }

    /**
     * Get the array with all classes parsed
     * 
     * @return the classesParsed the classes parsed
     */
    public Map<EasyJsonApiTypeToken, List<Class<?>>> getClassesParsed() {
        return classesParsed;
    }

    /**
     * Get the array with packages searched
     * 
     * @return the packagesSearched the packages searched
     */
    public String[] getPackagesSearched() {
        return packagesSearched;
    }

    /**
     * Set the packages to search
     * 
     * @param packages
     *            the packages needs to be search and parsing
     * @throws EasyJsonApiInvalidPackageException
     */
    public void setPackagesToSearch(String... packages) throws EasyJsonApiInvalidPackageException {

        this.packagesSearched = packages;

        for (String packageToSearch : packages) {
            try {
                ClassPath classpath = ClassPath.from(getClass().getClassLoader());

                List<Class<?>> attrClasses = new ArrayList<>();
                List<Class<?>> metaClasses = new ArrayList<>();
                List<Class<?>> metaRelationshipsClasses = new ArrayList<>();

                for (ClassPath.ClassInfo classInfo : classpath.getTopLevelClasses(packageToSearch)) {

                    Class<?> clazz = classInfo.load();

                    if (Assert.notNull(clazz.getAnnotation(Attributes.class))) {
                        attrClasses.add(clazz);
                    } else if (Assert.notNull(clazz.getAnnotation(Meta.class))) {
                        metaClasses.add(clazz);
                    } else if (Assert.notNull(clazz.getAnnotation(MetaRelationship.class))) {
                        metaRelationshipsClasses.add(clazz);
                    }
                }

                this.classesParsed.put(EasyJsonApiTypeToken.TOKEN_ATTR, attrClasses);
                this.classesParsed.put(EasyJsonApiTypeToken.TOKEN_META, metaClasses);
                this.classesParsed.put(EasyJsonApiTypeToken.TOKEN_META_RELATIONSHIP, metaRelationshipsClasses);

            } catch (IOException ex) {
                throw new EasyJsonApiInvalidPackageException("Invalid packages inserted!", ex);
            }
        }
    }

} public final class Data implements Cloneable {

    @SerializedName(value = "attributes")
    private final Object attr;

    @SerializedName(value = "id")
    private final String id;

    // @SerializedName(value = "links")
    // private final Object links;

    @SerializedName(value = "relationships")
    private Relationships rels;

    @SerializedName(value = "type")
    private final String type;

    public Data(String id, String type, Object attr) {
        Validation.checkValidObject(attr);
        this.id = id;
        this.type = type;
        this.attr = attr;
        this.rels = null;
    }

    public Data(String id, String type, Object attr, Relationships rels) {
        Validation.checkValidObject(attr);
        this.id = id;
        this.type = type;
        this.attr = attr;
        this.rels = rels;
    }

    /*
     * (non-Javadoc)
     * @see java.lang.Object#clone()
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    /**
     * @return the attr
     */
    public Object getAttr() {
        return attr;
    }

    /**
     * @return the id
     */
    public String getId() {
        return id;
    }

    /**
     * @return the rels
     */
    public Relationships getRels() {
        if (Assert.isNull(rels)) {
            this.rels = new Relationships();
        }
        return rels;
    }

    /**
     * @return the type
     */
    public String getType() {
        return type;
    }

} public class JsonApi {

    @SerializedName(value = "data")
    private List<Data> data;

    @SerializedName(value = "errors")
    private List<Error> errors;

    /**
     * Add {@link Data} inside the data list objects inside {@link JsonApi}
     * 
     * @param data
     *            the data to insert into {@link JsonApi}
     */
    public void addData(Data data) {

        if (Assert.isNull(this.data)) {
            this.data = new ArrayList<>();
        }

        this.data.add(data);

    }

    /**
     * Add {@link Error} inside the errors list objects inside {@link JsonApi}
     * 
     * @param error
     *            the error to insert into {@link JsonApi}
     */
    public void addError(Error error) {

        if (Assert.isNull(this.errors)) {
            this.errors = new ArrayList<>();
        }

        this.errors.add(error);

    }

    /**
     * Get the cloned data inside the JsonApi.
     * 
     * @return the data inside {@link JsonApi}
     */
    public List<Data> getData() {

        if (Assert.isNull(data)) {
            data = new ArrayList<>();
        }

        List<Data> cloneData = new ArrayList<>(this.data);

        return cloneData;
    }

    /**
     * Get the cloned error inside the JsonApi.
     * 
     * @return the errors inside {@link JsonApi}
     */
    public List<Error> getErrors() {

        if (Assert.isNull(errors)) {
            errors = new ArrayList<>();
        }

        List<Error> cloneErrors = new ArrayList<>(this.errors);

        return cloneErrors;
    }

    /*
     * (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "JsonApi [data=" + data + ", errors=" + errors + "]";
    }

} public class EntityDependencyTest {

    private Map<String, String> attr;

    /**
     * @return the attr
     */
    public Map<String, String> getAttr() {
        if (attr == null) {
            attr = new HashMap<>();
        }
        return attr;
    }

    /**
     * @param attr the attr to set
     */
    public void setAttr(Map<String, String> attr) {
        this.attr = attr;
    }

} @Attributes
public class EntityTestAttr1 {

    private String attr1;

    private BigDecimal attr2;

    private EntityDependencyTest attr3;

    /**
     * @return the attr1
     */
    public String getAttr1() {
        return attr1;
    }

    /**
     * @return the attr2
     */
    public BigDecimal getAttr2() {
        return attr2;
    }

    /**
     * @return the attr3
     */
    public EntityDependencyTest getAttr3() {
        return attr3;
    }

    /**
     * @param attr1 the attr1 to set
     */
    public void setAttr1(String attr1) {
        this.attr1 = attr1;
    }

    /**
     * @param attr2 the attr2 to set
     */
    public void setAttr2(BigDecimal attr2) {
        this.attr2 = attr2;
    }

    /**
     * @param attr3 the attr3 to set
     */
    public void setAttr3(EntityDependencyTest attr3) {
        this.attr3 = attr3;
    }

} @Attributes
public class EntityTestAttr2 {

    private String attr1;

    /**
     * @return the attr1
     */
    public String getAttr1() {
        return attr1;
    }

    /**
     * @param attr1 the attr1 to set
     */
    public void setAttr1(String attr1) {
        this.attr1 = attr1;
    }

} @Meta
public class EntityTestMeta {

    private String metadata;

    /**
     * @return the metadata
     */
    public String getMetadata() {
        return metadata;
    }

    /**
     * @param metadata the metadata to set
     */
    public void setMetadata(String metadata) {
        this.metadata = metadata;
    }

} @MetaRelationship
public class EntityTestMetaRelationship {

    private String count;

    /**
     * @return the count
     */
    public String getCount() {
        return count;
    }

    /**
     * @param count
     *            the count to set
     */
    public void setCount(String count) {
        this.count = count;
    }

} public class EasyJsonApiCastException extends EasyJsonApiEntityException {

    /**
     * UID Generated
     */
    private static final long serialVersionUID = -1976439574590963666L;

    public EasyJsonApiCastException(String message) {
        super(message);
    }

    public EasyJsonApiCastException(String message, Throwable cause) {
        super(message, cause);
    }

} public class EasyJsonApiInvalidPackageException extends EasyJsonApiException {

    /**
     * UID Generated
     */
    private static final long serialVersionUID = -1850836160599521183L;

    public EasyJsonApiInvalidPackageException(String message) {
        super(message);
    }

    public EasyJsonApiInvalidPackageException(String message, Throwable cause) {
        super(message, cause);
    }

}
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
import com.github.easyjsonapi.TestHelper;
import com.github.easyjsonapi.adapters.EasyJsonApiDeserializer;
import com.github.easyjsonapi.core.EasyJsonApiConfig;
import com.github.easyjsonapi.entities.Data;
import com.github.easyjsonapi.entities.JsonApi;
import com.github.easyjsonapi.entities.test.EntityDependencyTest;
import com.github.easyjsonapi.entities.test.EntityTestAttr1;
import com.github.easyjsonapi.entities.test.EntityTestAttr2;
import com.github.easyjsonapi.entities.test.EntityTestMeta;
import com.github.easyjsonapi.entities.test.EntityTestMetaRelationship;
import com.github.easyjsonapi.exceptions.EasyJsonApiCastException;
import com.github.easyjsonapi.exceptions.EasyJsonApiInvalidPackageException;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
/*
 * #%L
 * EasyJsonApi
 * %%
 * Copyright (C) 2016 EasyJsonApi
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package com.github.easyjsonapi.adapters;





public class EasyJsonApiDeserializerTest {

    private final String JSON_TEST_FOLDER = "jsonTest/EasyJsonApiDeserializerTest/";

    @Test
    public void genericDeserializerAttrTest() throws EasyJsonApiInvalidPackageException {

        JsonDeserializationContext deserializerContext = mock(JsonDeserializationContext.class);

        EasyJsonApiConfig config = new EasyJsonApiConfig("com.github.easyjsonapi.entities.test");
        EasyJsonApiDeserializer deserializer = new EasyJsonApiDeserializer();
        deserializer.setConfig(config);
        deserializer.setClassesUsed(EntityTestAttr1.class);

        EntityTestAttr1 attr = new EntityTestAttr1();
        attr.setAttr1("Miguel Teles");
        attr.setAttr2(new BigDecimal("2000"));
        EntityDependencyTest attr3 = new EntityDependencyTest();
        attr3.getAttr().put("title", "The best food");
        attr3.getAttr().put("pages", "4000");
        attr.setAttr3(attr3);

        when(deserializerContext.deserialize(Mockito.any(JsonElement.class), Mockito.any(Type.class))).thenReturn(attr);

        String jsonToTest = TestHelper.retriveJsonFile(JSON_TEST_FOLDER + "genericDeserializerAttrTest.json");