{
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/at/rags/morpheusexample/MainActivity.java:
--------------------------------------------------------------------------------
1 | package at.rags.morpheusexample;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 |
6 | public class MainActivity extends Activity {
7 | private static final String TAG = MainActivity.class.getSimpleName();
8 |
9 | @Override
10 | protected void onCreate(Bundle savedInstanceState) {
11 | super.onCreate(savedInstanceState);
12 | setContentView(R.layout.activity_main);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/morpheus/src/main/java/at/rags/morpheus/Logger.java:
--------------------------------------------------------------------------------
1 | package at.rags.morpheus;
2 |
3 | import android.util.Log;
4 |
5 | /**
6 | * Logger you can turn on and off.
7 | */
8 | public class Logger {
9 | private static final String TAG = "Morpheus";
10 | private static boolean debug = false;
11 |
12 | public static void debug(String message) {
13 | if (debug) {
14 | Log.d(TAG, message);
15 | }
16 | }
17 |
18 | public static void setDebug(boolean debug) {
19 | Logger.debug = debug;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/.idea/libraries/junit_4_12.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/morpheus/src/androidTest/java/at/rags/morpheus/Resources/Comment.java:
--------------------------------------------------------------------------------
1 | package at.rags.morpheus.Resources;
2 |
3 | import com.google.gson.annotations.SerializedName;
4 |
5 | import at.rags.morpheus.Resource;
6 |
7 | /**
8 | * Created by raphaelseher on 06/03/16.
9 | */
10 | public class Comment extends Resource {
11 | @SerializedName("body")
12 | private String body;
13 |
14 | public String getBody() {
15 | return body;
16 | }
17 |
18 | public void setBody(String body) {
19 | this.body = body;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/morpheus/src/androidTest/java/at/rags/morpheus/Resources/Location.java:
--------------------------------------------------------------------------------
1 | package at.rags.morpheus.Resources;
2 |
3 | /**
4 | * Created by raphaelseher on 22/04/16.
5 | */
6 | public class Location {
7 | private double lat;
8 | private double lon;
9 |
10 | public double getLat() {
11 | return lat;
12 | }
13 |
14 | public double getLon() {
15 | return lon;
16 | }
17 |
18 | public void setLon(double lon) {
19 | this.lon = lon;
20 | }
21 |
22 | public void setLat(double lat) {
23 | this.lat = lat;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/.idea/libraries/gson_2_6_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/objenesis_2_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 |
15 | # Gradle files
16 | .gradle/
17 | build/
18 |
19 | # Local configuration file (sdk path, etc)
20 | local.properties
21 |
22 | # Proguard folder generated by Eclipse
23 | proguard/
24 |
25 | # Log Files
26 | *.log
27 |
28 | # Android Studio Navigation editor temp files
29 | .navigation/
30 |
31 | # Android Studio captures folder
32 | captures/
33 | .idea/
34 | /.DS_Store
35 |
--------------------------------------------------------------------------------
/.idea/libraries/hamcrest_core_1_3.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/mockito_core_1_10_10.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 22
5 | buildToolsVersion "22.0.1"
6 |
7 | defaultConfig {
8 | applicationId "at.rags.morpheusexample"
9 | minSdkVersion 9
10 | targetSdkVersion 22
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(include: ['*.jar'], dir: 'libs')
24 | testCompile 'junit:junit:4.12'
25 | compile project(':morpheus')
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/morpheus/src/main/java/at/rags/morpheus/Annotations/Relationship.java:
--------------------------------------------------------------------------------
1 | package at.rags.morpheus.Annotations;
2 |
3 | import java.lang.annotation.Documented;
4 | import java.lang.annotation.ElementType;
5 | import java.lang.annotation.Retention;
6 | import java.lang.annotation.RetentionPolicy;
7 | import java.lang.annotation.Target;
8 |
9 | @Documented
10 | @Retention(RetentionPolicy.RUNTIME)
11 | @Target(ElementType.FIELD)
12 | /**
13 | * Define your json:api relationship.
14 | *
15 | *
16 | * {@code
17 | * @Relationship("author")
18 | * private Author author;
19 | *
20 | * @Relationship("comments")
21 | * private List comments;
22 | * }
23 | *
24 | */
25 | public @interface Relationship {
26 | String value();
27 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/raphaelseher/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/morpheus/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/raphaelseher/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
17 |
18 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/morpheus/src/main/res/raw/error.json:
--------------------------------------------------------------------------------
1 | {
2 | "errors": [
3 | {
4 | "status": "403",
5 | "source": { "pointer": "/data/attributes/secret-powers" },
6 | "detail": "Editing secret powers is not authorized on Sundays."
7 | },
8 | {
9 | "id": "1",
10 | "status": "422",
11 | "code": "2",
12 | "source": { "pointer": "/data/attributes/volume" ,
13 | "parameter" : "/data/attributes/battery"},
14 | "title": "some title",
15 | "detail": "Volume does not, in fact, go to 11.",
16 | "links": { "about" : "about.com"},
17 | "meta": { "test-meta":"yes"}
18 | },
19 | {
20 | "status": "500",
21 | "source": { "pointer": "/data/attributes/reputation" },
22 | "title": "The backend responded with an error",
23 | "detail": "Reputation service not responding after three requests."
24 | }
25 | ]
26 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/morpheus/src/main/res/raw/same_name_fields_product:
--------------------------------------------------------------------------------
1 | {
2 | "links": {
3 | "self": "http://example.com/products",
4 | "next": "http://example.com/products?page[site]=2",
5 | "last": "http://example.com/products?page[site]=89"
6 | },
7 | "data": [{
8 | "type": "products",
9 | "id": 123456,
10 | "attributes": {
11 | "product-name-description": "Really fancy stuff",
12 | "product-name": "Fancy new roboter",
13 | "categories": ["roboter", "home", "tech"],
14 | "price":999.75,
15 | "in-stock":9,
16 | "stores-availability":{
17 | "Store 1":true,
18 | "Store 2":true,
19 | "Store 3":false},
20 | "location":{
21 | "lat":14.202323,
22 | "lon":12.04995
23 | },
24 | "authors":[
25 | {
26 | "first-name":"raphael"
27 | }
28 | ]
29 | },
30 | "links": {
31 | "self": "http://example.com/products/123456"
32 | }
33 | }]
34 | }
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: android
2 | jdk: oraclejdk8
3 | sudo: false
4 | env:
5 | global:
6 | - ANDROID_API_LEVEL=22
7 | - EMULATOR_API_LEVEL=22
8 | - ANDROID_BUILD_TOOLS_VERSION=22.0.1
9 | - ANDROID_ABI=armeabi-v7a
10 | - ANDROID_TAG=google_apis
11 |
12 | before_script:
13 | - echo no | android create avd --force -n test -t "android-"$EMULATOR_API_LEVEL --abi $ANDROID_ABI --tag $ANDROID_TAG
14 | - emulator -avd test -no-audio -no-window &
15 | - android-wait-for-emulator
16 |
17 | script:
18 | - ./gradlew :morpheus:test :morpheus:connectedAndroidTest
19 |
20 | android:
21 | components:
22 | - platform-tools
23 | - tools
24 | - build-tools-$ANDROID_BUILD_TOOLS_VERSION
25 | - android-$ANDROID_API_LEVEL
26 |
27 | # Specify at least one system image,
28 | # if you need to run emulator(s) during your tests
29 | - sys-img-armeabi-v7a-google_apis-$ANDROID_API_LEVEL
30 | - sys-img-armeabi-v7a-google_apis-$EMULATOR_API_LEVEL
31 |
--------------------------------------------------------------------------------
/MorpheusExample.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/Morpheus.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/morpheus/src/main/res/raw/product.json:
--------------------------------------------------------------------------------
1 | {
2 | "links": {
3 | "self": "http://example.com/products",
4 | "next": "http://example.com/products?page[site]=2",
5 | "last": "http://example.com/products?page[site]=89"
6 | },
7 | "data": [{
8 | "type": "products",
9 | "id": 123456,
10 | "attributes": {
11 | "product-name": "Fancy new roboter",
12 | "categories": ["roboter", "home", "tech"],
13 | "price":999.75,
14 | "in-stock":9,
15 | "string-int":"5",
16 | "available":"true",
17 | "stores-availability":{
18 | "Store 1":true,
19 | "Store 2":true,
20 | "Store 3":false},
21 | "location":{
22 | "lat":14.202323,
23 | "lon":12.04995
24 | },
25 | "authors":[
26 | {
27 | "first-name":"raphael"
28 | }
29 | ],
30 | "times": [
31 | "9:14",
32 | "12 15"
33 | ]
34 | },
35 | "links": {
36 | "self": "http://example.com/products/123456"
37 | }
38 | }]
39 | }
--------------------------------------------------------------------------------
/morpheus/src/androidTest/java/at/rags/morpheus/Resources/Author.java:
--------------------------------------------------------------------------------
1 | package at.rags.morpheus.Resources;
2 |
3 | import com.google.gson.annotations.SerializedName;
4 |
5 | import at.rags.morpheus.Resource;
6 |
7 | /**
8 | * Created by raphaelseher on 05/03/16.
9 | */
10 | public class Author extends Resource {
11 |
12 | @SerializedName("first-name")
13 | private String firstName;
14 |
15 | @SerializedName("last-name")
16 | private String lastName;
17 |
18 | @SerializedName("twitter")
19 | private String twitterHandle;
20 |
21 | public String getFirstName() {
22 | return firstName;
23 | }
24 |
25 | public String getLastName() {
26 | return lastName;
27 | }
28 |
29 | public String getTwitterHandle() {
30 | return twitterHandle;
31 | }
32 |
33 | public void setFirstName(String firstName) {
34 | this.firstName = firstName;
35 | }
36 |
37 | public void setLastName(String lastName) {
38 | this.lastName = lastName;
39 | }
40 |
41 | public void setTwitterHandle(String twitterHandle) {
42 | this.twitterHandle = twitterHandle;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/morpheus/src/test/java/at/rags/morpheus/MorpheusUnitTest.java:
--------------------------------------------------------------------------------
1 | package at.rags.morpheus;
2 |
3 | import org.junit.Before;
4 | import org.junit.Test;
5 |
6 | import java.util.HashMap;
7 | import java.util.HashSet;
8 |
9 | import at.rags.morpheus.TestResources.Author;
10 |
11 | import static org.junit.Assert.*;
12 | import static org.mockito.Mockito.mock;
13 | import static org.mockito.Mockito.when;
14 |
15 | /**
16 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
17 | */
18 | public class MorpheusUnitTest {
19 |
20 | private Morpheus morpheus;
21 |
22 | @Before
23 | public void setup() {
24 | morpheus = new Morpheus();
25 |
26 | HashMap mockMap = mock(HashMap.class);
27 | when(mockMap.get("authors")).thenReturn(Author.class);
28 | HashSet set = new HashSet<>();
29 | set.add("authors");
30 | when(mockMap.keySet()).thenReturn(set);
31 | Deserializer.setRegisteredClasses(mockMap);
32 | }
33 |
34 | @Test
35 | public void testInit() throws Exception {
36 | Morpheus morpheus = new Morpheus();
37 | assertNotNull(morpheus);
38 | }
39 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Raphael Seher
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/morpheus/src/androidTest/java/at/rags/morpheus/Resources/Article.java:
--------------------------------------------------------------------------------
1 | package at.rags.morpheus.Resources;
2 |
3 | import com.google.gson.annotations.SerializedName;
4 |
5 | import java.util.List;
6 |
7 | import at.rags.morpheus.Annotations.Relationship;
8 | import at.rags.morpheus.Resource;
9 |
10 | public class Article extends Resource {
11 |
12 | @SerializedName("title")
13 | private String title;
14 |
15 | @Relationship("author")
16 | private Author author;
17 |
18 | @Relationship("comments")
19 | private List comments;
20 |
21 | private List tags;
22 |
23 | public String getTitle() {
24 | return title;
25 | }
26 |
27 | public Author getAuthor() {
28 | return author;
29 | }
30 |
31 | public List getComments() {
32 | return comments;
33 | }
34 |
35 | public List getTags() {
36 | return tags;
37 | }
38 |
39 | public void setTitle(String title) {
40 | this.title = title;
41 | }
42 |
43 | public void setAuthor(Author author) {
44 | this.author = author;
45 | }
46 |
47 | public void setComments(List comments) {
48 | this.comments = comments;
49 | }
50 |
51 | public void setTags(List tags) {
52 | this.tags = tags;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [0.5.2](https://github.com/xamoom/Morpheus/compare/v0.5.1...v0.5.2)
4 |
5 | * Fixed wrong nulling of relationships
6 | * Before: nulled the complete relationship
7 | * Now: nulling the data of the relationship
8 |
9 | ## [0.5.1](https://github.com/xamoom/Morpheus/compare/v0.5.0...v0.5.1)
10 |
11 | * Added nulling relationships to delete them
12 |
13 | ## [0.5.0](https://github.com/xamoom/Morpheus/compare/v0.4.3...v0.5.0)
14 |
15 | * Added serializing of objects
16 | * Serialize resources with their relations
17 | * Add included if needed
18 |
19 | * Fixed [issue #12](https://github.com/xamoom/Morpheus/issues/12)
20 |
21 | ## [0.4.3](https://github.com/xamoom/Morpheus/compare/v0.4.2...v0.4.3)
22 |
23 | * Changed mapAttribute to use gson for primitive types
24 |
25 | ## [0.4.2](https://github.com/xamoom/Morpheus/compare/v0.4.1...v0.4.2)
26 |
27 | * Fixed null fields after parsing [pull #10](https://github.com/xamoom/Morpheus/pull/10)
28 |
29 | ## [0.4.1](https://github.com/xamoom/Morpheus/compare/v0.4.0...v0.4.1)
30 |
31 | * Removed e.printStackTrace()
32 |
33 | ## 0.4.0
34 |
35 | * Integrated Gson for better object mapping (Thanks to [@ycoupe](https://github.com/ycoupe))
36 | * Changed from own annotation 'SerializeName' to Gsons 'SerializedName'
37 |
38 | * Refactored Links to be public (Thanks to [@openkwaky](https://github.com/openkwaky))
39 |
40 | * Added Travis as CI
41 |
--------------------------------------------------------------------------------
/morpheus/src/main/java/at/rags/morpheus/JsonApiObject.java:
--------------------------------------------------------------------------------
1 | package at.rags.morpheus;
2 |
3 | import android.util.ArrayMap;
4 |
5 | import java.util.HashMap;
6 | import java.util.List;
7 | import java.util.Objects;
8 |
9 | public class JsonApiObject {
10 |
11 | private Resource resource;
12 | private List resources;
13 | private List included;
14 | private HashMap meta;
15 | private List errors;
16 | private Links links;
17 |
18 | //getters & setters
19 |
20 | public Resource getResource() {
21 | return resource;
22 | }
23 |
24 | public void setResource(Resource resource) {
25 | this.resource = resource;
26 | }
27 |
28 | public List getResources() {
29 | return resources;
30 | }
31 |
32 | public void setResources(List resources) {
33 | this.resources = resources;
34 | }
35 |
36 | public List getIncluded() {
37 | return included;
38 | }
39 |
40 | public void setIncluded(List included) {
41 | this.included = included;
42 | }
43 |
44 | public HashMap getMeta() {
45 | return meta;
46 | }
47 |
48 | public void setMeta(HashMap meta) {
49 | this.meta = meta;
50 | }
51 |
52 | public List getErrors() {
53 | return errors;
54 | }
55 |
56 | public void setErrors(List errors) {
57 | this.errors = errors;
58 | }
59 |
60 | public Links getLinks() {
61 | return links;
62 | }
63 |
64 | public void setLinks(Links links) {
65 | this.links = links;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/morpheus/src/main/java/at/rags/morpheus/Links.java:
--------------------------------------------------------------------------------
1 | package at.rags.morpheus;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 |
6 | /**
7 | * Links object.
8 | *
9 | * @see JsonApiObject
10 | * @see Resource
11 | * @author kwaky
12 | */
13 | public class Links {
14 | private String selfLink;
15 | private String related; // TODO: related can also have a meta
16 | private String first;
17 | private String last;
18 | private String prev;
19 | private String next;
20 | private String about;
21 |
22 | public Links() {
23 | }
24 |
25 | public String getSelfLink() {
26 | return selfLink;
27 | }
28 |
29 | public void setSelfLink(String selfLink) {
30 | this.selfLink = selfLink;
31 | }
32 |
33 | public String getRelated() {
34 | return related;
35 | }
36 |
37 | public void setRelated(String related) {
38 | this.related = related;
39 | }
40 |
41 | public String getFirst() {
42 | return first;
43 | }
44 |
45 | public void setFirst(String first) {
46 | this.first = first;
47 | }
48 |
49 | public String getLast() {
50 | return last;
51 | }
52 |
53 | public void setLast(String last) {
54 | this.last = last;
55 | }
56 |
57 | public String getPrev() {
58 | return prev;
59 | }
60 |
61 | public void setPrev(String prev) {
62 | this.prev = prev;
63 | }
64 |
65 | public String getNext() {
66 | return next;
67 | }
68 |
69 | public void setNext(String next) {
70 | this.next = next;
71 | }
72 |
73 | public String getAbout() {
74 | return about;
75 | }
76 |
77 | public void setAbout(String about) {
78 | this.about = about;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/morpheus/src/main/java/at/rags/morpheus/Resource.java:
--------------------------------------------------------------------------------
1 | package at.rags.morpheus;
2 |
3 | import java.util.ArrayList;
4 | import java.util.HashMap;
5 |
6 | /**
7 | * Extend this resource to your custom Object you want to map.
8 | * You can set custom json object names and relationships via the provided annotations.
9 | *
10 | * {@code
11 | * public class Article extends Resource { ... }
12 | * }
13 | *
14 | * @see com.google.gson.annotations.SerializedName
15 | * @see at.rags.morpheus.Annotations.Relationship
16 | */
17 | public class Resource {
18 | private String Id;
19 | private Links links;
20 | private HashMap meta;
21 |
22 | private ArrayList nullableRelationships = new ArrayList<>();
23 |
24 | public Resource() {
25 | }
26 |
27 | public HashMap getMeta() {
28 | return meta;
29 | }
30 |
31 | public void setMeta(HashMap meta) {
32 | this.meta = meta;
33 | }
34 |
35 | public Links getLinks() {
36 | return links;
37 | }
38 |
39 | public void setLinks(Links links) {
40 | this.links = links;
41 | }
42 |
43 | public String getId() {
44 | return Id;
45 | }
46 |
47 | public void setId(String id) {
48 | Id = id;
49 | }
50 |
51 | public ArrayList getNullableRelationships() {
52 | return nullableRelationships;
53 | }
54 |
55 | public void resetNullableRelationships() {
56 | nullableRelationships.clear();
57 | }
58 |
59 | /**
60 | * Add here your relationship name, if you want to null it while serializing.
61 | * This can be used to remove relationships from your object.
62 | *
63 | * @param relationshipName Name of your relationship.
64 | */
65 | public void addRelationshipToNull(String relationshipName) {
66 | if (relationshipName == null) {
67 | return;
68 | }
69 |
70 | nullableRelationships.add(relationshipName);
71 | }
72 | }
73 |
74 |
--------------------------------------------------------------------------------
/morpheus/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | def versionString = "0.5.2"
4 |
5 | ext {
6 | bintrayRepo = 'maven'
7 | bintrayName = 'Morpheus'
8 | publishedGroupId = 'com.xamoom.android'
9 | libraryName = 'Morpheus'
10 | artifact = 'morpheus'
11 |
12 | libraryDescription = 'Morpheus is a JSONAPI deserializer for android that uses java reflection. You can define your own java classes to deserialize.'
13 |
14 | siteUrl = 'http://xamoom.github.io/Morpheus/'
15 | gitUrl = 'https://github.com/xamoom/Morpheus.git'
16 |
17 | libraryVersion = versionString
18 | developerId = 'xamoom-raphael'
19 | developerName = 'Raphael Seher'
20 | developerEmail = 'raphael@xamoom.com'
21 |
22 | licenseName = 'The MIT License'
23 | licenseUrl = 'https://opensource.org/licenses/MIT'
24 | allLicenses = ["MIT"]
25 | }
26 |
27 | android {
28 | compileSdkVersion 22
29 | buildToolsVersion "22.0.1"
30 |
31 | defaultConfig {
32 | minSdkVersion 9
33 | targetSdkVersion 22
34 | versionCode 6
35 | versionName versionString
36 | }
37 | buildTypes {
38 | release {
39 | minifyEnabled false
40 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
41 | }
42 | }
43 | testOptions {
44 | unitTests.returnDefaultValues = true
45 | }
46 | }
47 |
48 | dependencies {
49 | compile fileTree(dir: 'libs', include: ['*.jar'])
50 | androidTestCompile 'junit:junit:4.12'
51 | androidTestCompile 'org.mockito:mockito-core:1.10.10'
52 |
53 | testCompile 'junit:junit:4.12'
54 | testCompile 'org.mockito:mockito-core:1.10.10'
55 |
56 | compile 'com.google.code.gson:gson:2.6.2'
57 | }
58 |
59 | //apply from: 'https://raw.githubusercontent.com/xamoom-raphael/JCenter/master/installv1.gradle'
60 | //apply from: 'https://raw.githubusercontent.com/xamoom-raphael/JCenter/master/bintrayv1.gradle'
--------------------------------------------------------------------------------
/app/src/main/res/raw/articles.json:
--------------------------------------------------------------------------------
1 | {
2 | "links": {
3 | "self": "http://example.com/articles",
4 | "next": "http://example.com/articles?page[offset]=2",
5 | "last": "http://example.com/articles?page[offset]=10"
6 | },
7 | "data": [{
8 | "type": "articles",
9 | "id": "1",
10 | "attributes": {
11 | "title": "JSON API paints my bikeshed!"
12 | },
13 | "relationships": {
14 | "author": {
15 | "links": {
16 | "self": "http://example.com/articles/1/relationships/author",
17 | "related": "http://example.com/articles/1/author"
18 | },
19 | "data": { "type": "people", "id": "9" }
20 | },
21 | "comments": {
22 | "links": {
23 | "self": "http://example.com/articles/1/relationships/comments",
24 | "related": "http://example.com/articles/1/comments"
25 | },
26 | "data": [
27 | { "type": "comments", "id": "5" },
28 | { "type": "comments", "id": "12" }
29 | ]
30 | }
31 | },
32 | "links": {
33 | "self": "http://example.com/articles/1"
34 | }
35 | }],
36 | "included": [{
37 | "type": "people",
38 | "id": "9",
39 | "attributes": {
40 | "first-name": "Dan",
41 | "last-name": "Gebhardt",
42 | "twitter": "dgeb"
43 | },
44 | "links": {
45 | "self": "http://example.com/people/9"
46 | }
47 | }, {
48 | "type": "comments",
49 | "id": "5",
50 | "attributes": {
51 | "body": "First!"
52 | },
53 | "relationships": {
54 | "author": {
55 | "data": { "type": "people", "id": "2" }
56 | }
57 | },
58 | "links": {
59 | "self": "http://example.com/comments/5"
60 | }
61 | }, {
62 | "type": "comments",
63 | "id": "12",
64 | "attributes": {
65 | "body": "I like XML better"
66 | },
67 | "relationships": {
68 | "author": {
69 | "data": { "type": "people", "id": "9" }
70 | }
71 | },
72 | "links": {
73 | "self": "http://example.com/comments/12"
74 | }
75 | }]
76 | }
--------------------------------------------------------------------------------
/morpheus/src/test/java/at/rags/morpheus/TestResources/Article.java:
--------------------------------------------------------------------------------
1 | package at.rags.morpheus.TestResources;
2 |
3 | import android.util.ArrayMap;
4 |
5 | import com.google.gson.annotations.SerializedName;
6 |
7 | import java.util.List;
8 |
9 | import at.rags.morpheus.Annotations.Relationship;
10 | import at.rags.morpheus.Resource;
11 |
12 | public class Article extends Resource {
13 |
14 | private String title;
15 | @SerializedName("public")
16 | private boolean publicStatus;
17 | private List tags;
18 | private ArrayMap map;
19 | private int version;
20 | private double price;
21 |
22 | @Relationship("author")
23 | private Author author;
24 |
25 | @Relationship("authors")
26 | private List authors;
27 |
28 | //getter & setter
29 |
30 | public String getTitle() {
31 | return title;
32 | }
33 |
34 | public void setTitle(String title) {
35 | this.title = title;
36 | }
37 |
38 | public boolean getPublicStatus() {
39 | return publicStatus;
40 | }
41 |
42 | public void setPublicStatus(boolean publicStatus) {
43 | this.publicStatus = publicStatus;
44 | }
45 |
46 | public List getTags() {
47 | return tags;
48 | }
49 |
50 | public void setTags(List tags) {
51 | this.tags = tags;
52 | }
53 |
54 | public int getVersion() {
55 | return version;
56 | }
57 |
58 | public void setVersion(int version) {
59 | this.version = version;
60 | }
61 |
62 | public double getPrice() {
63 | return price;
64 | }
65 |
66 | public ArrayMap getMap() {
67 | return map;
68 | }
69 |
70 | public void setMap(ArrayMap map) {
71 | this.map = map;
72 | }
73 |
74 | public Author getAuthor() {
75 | return author;
76 | }
77 |
78 | public List getAuthors() {
79 | return authors;
80 | }
81 |
82 | public void setPrice(double price) {
83 | this.price = price;
84 | }
85 |
86 | public void setAuthor(Author author) {
87 | this.author = author;
88 | }
89 |
90 | public void setAuthors(List authors) {
91 | this.authors = authors;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/morpheus/src/main/res/raw/articles.json:
--------------------------------------------------------------------------------
1 | {
2 | "meta":{
3 | "testmeta": "yes"
4 | },
5 | "links": {
6 | "self": "http://example.com/articles",
7 | "next": "http://example.com/articles?page[offset]=2",
8 | "last": "http://example.com/articles?page[offset]=10"
9 | },
10 | "data": [{
11 | "type": "articles",
12 | "id": "1",
13 | "attributes": {
14 | "title": "JSON API paints my bikeshed!"
15 | },
16 | "relationships": {
17 | "author": {
18 | "links": {
19 | "self": "http://example.com/articles/1/relationships/author",
20 | "related": "http://example.com/articles/1/author"
21 | },
22 | "data": { "type": "people", "id": "9" }
23 | },
24 | "comments": {
25 | "links": {
26 | "self": "http://example.com/articles/1/relationships/comments",
27 | "related": "http://example.com/articles/1/comments"
28 | },
29 | "data": [
30 | { "type": "comments", "id": "5" },
31 | { "type": "comments", "id": "12" }
32 | ]
33 | }
34 | },
35 | "links": {
36 | "self": "http://example.com/articles/1"
37 | }
38 | }],
39 | "included": [
40 | {
41 | "type": "people",
42 | "id": "9",
43 | "attributes": {
44 | "first-name": "Dan",
45 | "last-name": "Gebhardt",
46 | "twitter": "dgeb"
47 | },
48 | "links": {
49 | "self": "http://example.com/people/9"
50 | }
51 | }, {
52 | "type": "comments",
53 | "id": "5",
54 | "attributes": {
55 | "body": "First!"
56 | },
57 | "relationships": {
58 | "author": {
59 | "data": { "type": "people", "id": "2" }
60 | }
61 | },
62 | "links": {
63 | "self": "http://example.com/comments/5"
64 | }
65 | }, {
66 | "type": "comments",
67 | "id": "12",
68 | "attributes": {
69 | "body": "I like XML better"
70 | },
71 | "relationships": {
72 | "author": {
73 | "data": { "type": "people", "id": "9" }
74 | }
75 | },
76 | "links": {
77 | "self": "http://example.com/comments/12"
78 | }
79 | }]
80 | }
--------------------------------------------------------------------------------
/morpheus/src/main/res/raw/article.json:
--------------------------------------------------------------------------------
1 | {
2 | "links": {
3 | "self": "http://example.com/articles",
4 | "next": "http://example.com/articles?page[offset]=2",
5 | "last": "http://example.com/articles?page[offset]=10"
6 | },
7 | "data": {
8 | "type": "articles",
9 | "id": "1",
10 | "attributes": {
11 | "title": "JSON API paints my bikeshed!",
12 | "tags": ["main", "dev"]
13 | },
14 | "meta": {
15 | "test-meta":"yes"
16 | },
17 | "relationships": {
18 | "author": {
19 | "links": {
20 | "self": "http://example.com/articles/1/relationships/author",
21 | "related": "http://example.com/articles/1/author"
22 | },
23 | "data": { "type": "people", "id": "9" }
24 | },
25 | "comments": {
26 | "links": {
27 | "self": "http://example.com/articles/1/relationships/comments",
28 | "related": "http://example.com/articles/1/comments"
29 | },
30 | "data": [
31 | { "type": "comments", "id": "5" },
32 | { "type": "comments", "id": "12" }
33 | ]
34 | }
35 | },
36 | "links": {
37 | "self": "http://example.com/articles/1"
38 | }
39 | },
40 | "included": [{
41 | "type": "people",
42 | "id": "9",
43 | "attributes": {
44 | "first-name": "Dan",
45 | "last-name": "Gebhardt",
46 | "twitter": "dgeb"
47 | },
48 | "links": {
49 | "self": "http://example.com/people/9"
50 | }
51 | }, {
52 | "type": "comments",
53 | "id": "5",
54 | "attributes": {
55 | "body": "First!"
56 | },
57 | "relationships": {
58 | "author": {
59 | "data": { "type": "people", "id": "2" }
60 | }
61 | },
62 | "links": {
63 | "self": "http://example.com/comments/5"
64 | }
65 | }, {
66 | "type": "comments",
67 | "id": "12",
68 | "attributes": {
69 | "body": "I like XML better"
70 | },
71 | "relationships": {
72 | "author": {
73 | "data": { "type": "people", "id": "9" }
74 | }
75 | },
76 | "links": {
77 | "self": "http://example.com/comments/12"
78 | }
79 | }]
80 | }
--------------------------------------------------------------------------------
/morpheus/src/androidTest/java/at/rags/morpheus/Resources/Product.java:
--------------------------------------------------------------------------------
1 | package at.rags.morpheus.Resources;
2 |
3 | import com.google.gson.annotations.SerializedName;
4 |
5 | import java.util.HashMap;
6 | import java.util.List;
7 |
8 | import at.rags.morpheus.Resource;
9 |
10 | /**
11 | * Created by raphaelseher on 08/03/16.
12 | */
13 | public class Product extends Resource {
14 | @SerializedName("product-name")
15 | private String name;
16 | private List categories;
17 | private double price;
18 | @SerializedName("in-stock")
19 | private int inStock;
20 | @SerializedName("stores-availability")
21 | private HashMap availability;
22 | private Location location;
23 | private List authors;
24 | private List times;
25 |
26 | public String getName() {
27 | return name;
28 | }
29 |
30 | public List getCategories() {
31 | return categories;
32 | }
33 |
34 | public double getPrice() {
35 | return price;
36 | }
37 |
38 | public int getInStock() {
39 | return inStock;
40 | }
41 |
42 | public HashMap getAvailability() {
43 | return availability;
44 | }
45 |
46 | public Location getLocation() {
47 | return location;
48 | }
49 |
50 | public List getAuthors() {
51 | return authors;
52 | }
53 |
54 | public void setName(String name) {
55 | this.name = name;
56 | }
57 |
58 | public void setCategories(List categories) {
59 | this.categories = categories;
60 | }
61 |
62 | public void setPrice(double price) {
63 | this.price = price;
64 | }
65 |
66 | public void setInStock(int inStock) {
67 | this.inStock = inStock;
68 | }
69 |
70 | public void setAvailability(HashMap availability) {
71 | this.availability = availability;
72 | }
73 |
74 | public void setLocation(Location location) {
75 | this.location = location;
76 | }
77 |
78 | public void setAuthors(List authors) {
79 | this.authors = authors;
80 | }
81 |
82 | public List getTimes() {
83 | return times;
84 | }
85 |
86 | public void setTimes(List times) {
87 | this.times = times;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/morpheus/src/main/java/at/rags/morpheus/Error.java:
--------------------------------------------------------------------------------
1 | package at.rags.morpheus;
2 |
3 | import android.util.ArrayMap;
4 |
5 | import java.util.HashMap;
6 |
7 | /**
8 | * JSON:API error object.
9 | */
10 | public class Error {
11 | private String id;
12 | private String status;
13 | private String code;
14 | private String title;
15 | private String detail;
16 | private Source source;
17 | private ErrorLinks links;
18 | private HashMap meta;
19 |
20 | public HashMap getMeta() {
21 | return meta;
22 | }
23 |
24 | public void setMeta(HashMap meta) {
25 | this.meta = meta;
26 | }
27 |
28 | public String getId() {
29 | return id;
30 | }
31 |
32 | public void setId(String id) {
33 | this.id = id;
34 | }
35 |
36 | public ErrorLinks getLinks() {
37 | return links;
38 | }
39 |
40 | public void setLinks(ErrorLinks linkss) {
41 | this.links = linkss;
42 | }
43 |
44 | public String getStatus() {
45 | return status;
46 | }
47 |
48 | public void setStatus(String status) {
49 | this.status = status;
50 | }
51 |
52 | public String getCode() {
53 | return code;
54 | }
55 |
56 | public void setCode(String code) {
57 | this.code = code;
58 | }
59 |
60 | public String getTitle() {
61 | return title;
62 | }
63 |
64 | public void setTitle(String title) {
65 | this.title = title;
66 | }
67 |
68 | public String getDetail() {
69 | return detail;
70 | }
71 |
72 | public void setDetail(String detail) {
73 | this.detail = detail;
74 | }
75 |
76 | public Source getSource() {
77 | return source;
78 | }
79 |
80 | public void setSource(Source source) {
81 | this.source = source;
82 | }
83 | }
84 |
85 | class Source {
86 | private String parameter;
87 | private String pointer;
88 |
89 | public String getPointer() {
90 | return pointer;
91 | }
92 |
93 | public void setPointer(String pointer) {
94 | this.pointer = pointer;
95 | }
96 |
97 | public String getParameter() {
98 | return parameter;
99 | }
100 |
101 | public void setParameter(String parameter) {
102 | this.parameter = parameter;
103 | }
104 | }
105 |
106 | class ErrorLinks {
107 | private String about;
108 |
109 | public String getAbout() {
110 | return about;
111 | }
112 |
113 | public void setAbout(String about) {
114 | this.about = about;
115 | }
116 | }
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/morpheus/src/main/java/at/rags/morpheus/Serializer.java:
--------------------------------------------------------------------------------
1 | package at.rags.morpheus;
2 |
3 | import android.util.ArrayMap;
4 | import android.util.Log;
5 |
6 | import com.google.gson.annotations.SerializedName;
7 |
8 | import java.lang.annotation.Annotation;
9 | import java.lang.reflect.Field;
10 | import java.util.ArrayList;
11 | import java.util.Dictionary;
12 | import java.util.HashMap;
13 | import java.util.Map;
14 | import java.util.Objects;
15 |
16 | import at.rags.morpheus.Annotations.Relationship;
17 |
18 | /**
19 | */
20 | public class Serializer {
21 |
22 | /**
23 | * Return objects fields as dictionary with fieldName as key
24 | * and fieldObject as value.
25 | *
26 | * @param resource A morpheus resource.
27 | * @return hashMap of field names and values.
28 | */
29 | public HashMap getFieldsAsDictionary(Resource resource) {
30 | HashMap fieldDict = null;
31 |
32 | for (Field field : resource.getClass().getDeclaredFields()) {
33 | String fieldName = null;
34 |
35 | if (field.isAnnotationPresent(Relationship.class)) {
36 | continue;
37 | }
38 |
39 | Object fieldValue = null;
40 | try {
41 | field.setAccessible(true);
42 | fieldValue = field.get(resource);
43 | if (fieldValue == null) {
44 | continue;
45 | }
46 | } catch (IllegalAccessException e) {
47 | Logger.debug("Cannot access field: " + fieldName + ".");
48 | }
49 |
50 | if (field.isAnnotationPresent(SerializedName.class)) {
51 | Annotation annotation = field.getAnnotation(SerializedName.class);
52 | SerializedName serializeName = (SerializedName) annotation;
53 | fieldName = serializeName.value();
54 | } else {
55 | fieldName = field.getName();
56 | }
57 |
58 | if (fieldDict == null) {
59 | fieldDict = new HashMap<>();
60 | }
61 |
62 | fieldDict.put(fieldName, fieldValue);
63 | }
64 |
65 | return fieldDict;
66 | }
67 |
68 | public HashMap getRelationships(Resource resource) {
69 | HashMap relationships = new HashMap<>();
70 |
71 | for (Field field : resource.getClass().getDeclaredFields()) {
72 | if (field.isAnnotationPresent(Relationship.class)) {
73 | Annotation annotation = field.getAnnotation(Relationship.class);
74 | Relationship relationship = (Relationship) annotation;
75 |
76 | field.setAccessible(true);
77 | try {
78 | relationships.put(relationship.value(), field.get(resource));
79 | } catch (IllegalAccessException e) {
80 | Logger.debug("Cannot access field: " + field.getName() + ".");
81 | }
82 | }
83 | }
84 |
85 | return relationships;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Morpheus
2 |
3 | [ ](https://bintray.com/xamoom/maven/Morpheus/_latestVersion)
4 | [](https://travis-ci.org/xamoom/Morpheus)
5 |
6 | Morpheus is a [JSONAPI](http://jsonapi.org/) deserializer for android that uses java reflection.
7 | You can define your own java classes to deserialize.
8 |
9 | Take a look at the [documentation](http://xamoom.github.io/Morpheus/docs/0.5.1/index.html).
10 |
11 | ## Install
12 |
13 | ```java
14 | compile 'com.xamoom.android:morpheus:0.5.2'
15 | ```
16 |
17 | ## Usage
18 |
19 | Prepare your resources
20 |
21 | 1. extend Resource
22 | 2. Use @SerializedName() annotation when your field name differs from the json.
23 | 3. Create relationship mapping with the @Relationship() annotation.
24 |
25 | ```java
26 | public class Article extends Resource {
27 | @SerializedName("article-title")
28 | private String title;
29 | @Relationship("author")
30 | private Author author;
31 | @Relationship("comments")
32 | private List comments;
33 | }
34 | ```
35 | ### Deserialize
36 |
37 | 1. Create a Morpheus instance
38 | 2. Register your resources
39 | 3. parse your JSON string
40 |
41 | ```java
42 | Morpheus morpheus = new Morpheus();
43 | //register your resources
44 | Deserializer.registerResourceClass("articles", Article.class);
45 | Deserializer.registerResourceClass("people", Author.class);
46 | Deserializer.registerResourceClass("comments", Comment.class);
47 | JsonApiObject jsonApiObject =
48 | morpheus.parse(loadJSONFromAsset(R.raw.articles));
49 |
50 | Article article = (Article)jsonApiObject.getResources().get(0);
51 | Log.v(TAG, "Article Id: " + article.getId())
52 | ```
53 |
54 | ### Serialize
55 |
56 | ```java
57 | Morpheus morpheus = new Morpheus();
58 | Deserializer.registerResourceClass("products", Product.class);
59 |
60 | JsonApiObject jsonApiObject = new JsonApiObject();
61 | jsonApiObject.setResource(product);
62 |
63 | String json = morpheus.createJson(jsonApiObject, false);
64 | ```
65 |
66 | Delete an relationship:
67 | ```java
68 | article.addRelationshipToNull("author");
69 |
70 | JsonApiObject jsonApiObject = new JsonApiObject();
71 | jsonApiObject.setResource(article);
72 |
73 | String articleJson = morpheus.createJson(jsonApiObject, false);
74 |
75 | ```
76 |
77 | # Development status
78 | Morpheus can:
79 |
80 | * deserialize data object or array
81 | * deserialize relationships
82 | * map includes to relationships
83 | * deserialize links, meta, errors
84 | * serialize resources with their relationships and includes
85 |
86 | # Data Attribute Mapping
87 | At the moment Morpheus maps
88 |
89 | * Strings -> `String`
90 | * Floats -> `double`
91 | * Booleans -> `boolean`
92 | * JSONArrays -> `List` (with Gson)
93 | * JSONObject -> `HashMap` (with Gson)
94 |
95 | You can write your own AttributeMapper by extending `AttributeMapper.java` and initialize Morpheus with your mapper.
96 |
97 | # Contribution
98 | If you want to contribute: make your changes and do a pull request.
99 |
--------------------------------------------------------------------------------
/morpheus/src/test/java/at/rags/morpheus/SerializerUnitTest.java:
--------------------------------------------------------------------------------
1 | package at.rags.morpheus;
2 |
3 | import android.annotation.TargetApi;
4 | import android.os.Build;
5 | import android.test.suitebuilder.annotation.SmallTest;
6 | import android.util.ArrayMap;
7 |
8 | import org.junit.Before;
9 | import org.junit.Test;
10 | import org.junit.runner.RunWith;
11 | import org.junit.runners.JUnit4;
12 |
13 | import java.util.ArrayList;
14 | import java.util.Dictionary;
15 | import java.util.HashMap;
16 | import java.util.Map;
17 | import java.util.Objects;
18 |
19 | import at.rags.morpheus.TestResources.Article;
20 | import at.rags.morpheus.TestResources.Author;
21 |
22 | import static junit.framework.Assert.assertEquals;
23 | import static junit.framework.Assert.assertNotNull;
24 | import static junit.framework.Assert.assertNull;
25 |
26 | /**
27 | * Created by raphaelseher on 16/09/16.
28 | */
29 | @RunWith(JUnit4.class)
30 | public class SerializerUnitTest {
31 | private Serializer serializer;
32 |
33 | @Before
34 | public void setup() {
35 | serializer = new Serializer();
36 | }
37 |
38 | @TargetApi(Build.VERSION_CODES.KITKAT)
39 | @Test
40 | public void testGetFieldsAsDictionary() {
41 | Article article = new Article();
42 | article.setTitle("title");
43 | article.setPublicStatus(true);
44 | ArrayList tags = new ArrayList();
45 | tags.add("tag1");
46 | article.setTags(tags);
47 | ArrayMap testmap = new ArrayMap<>();
48 | testmap.put("key", "value");
49 | article.setMap(testmap);
50 | article.setVersion(1);
51 | article.setPrice(1.0);
52 |
53 | Map checkMap = new HashMap<>();
54 | checkMap.put("price", 1.0);
55 | checkMap.put("public", "true");
56 | checkMap.put("title", "title");
57 | checkMap.put("map", testmap);
58 | checkMap.put("version", 1);
59 | checkMap.put("tags", tags);
60 |
61 | Map map = serializer.getFieldsAsDictionary(article);
62 |
63 | assertNotNull(map);
64 | assertEquals(checkMap.toString(), map.toString());
65 | }
66 |
67 | @Test
68 | public void testGetFieldsAsDictionaryWithoutAttributes() {
69 | Author author = new Author();
70 | author.setId("id");
71 |
72 | Map checkMap = new HashMap<>();
73 | checkMap.put("id", "id");
74 |
75 | Map map = serializer.getFieldsAsDictionary(author);
76 |
77 | assertNull(map);
78 | }
79 |
80 | @Test
81 | public void testGetRelationships() {
82 | Article article = new Article();
83 | Author author = new Author();
84 | article.setAuthor(author);
85 |
86 | ArrayList authors = new ArrayList<>();
87 | authors.add(author);
88 | authors.add(author);
89 | article.setAuthors(authors);
90 |
91 | HashMap checkMap = new HashMap<>();
92 | checkMap.put("author", author);
93 | checkMap.put("authors", authors);
94 |
95 |
96 | HashMap output = serializer.getRelationships(article);
97 |
98 |
99 | assertEquals(output.toString(), checkMap.toString());
100 | }
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/morpheus/src/test/java/at/rags/morpheus/DeserializerTest.java:
--------------------------------------------------------------------------------
1 | package at.rags.morpheus;
2 |
3 | import android.util.ArrayMap;
4 |
5 | import org.junit.Test;
6 |
7 | import java.util.HashMap;
8 |
9 | import at.rags.morpheus.Exceptions.NotExtendingResourceException;
10 | import at.rags.morpheus.TestResources.FalseResource;
11 | import at.rags.morpheus.TestResources.InterfaceArticle;
12 | import at.rags.morpheus.TestResources.Article;
13 | import at.rags.morpheus.TestResources.MultiExtendResource;
14 |
15 | import static org.junit.Assert.*;
16 | import static org.mockito.Mockito.mock;
17 | import static org.mockito.Mockito.when;
18 |
19 | /**
20 | * Created by raphaelseher on 11/03/16.
21 | */
22 | public class DeserializerTest {
23 |
24 | @Test
25 | public void testCreateObjectFromString() throws Exception {
26 | HashMap mockMap = mock(HashMap.class);
27 | when(mockMap.get("articles")).thenReturn(Article.class);
28 | Deserializer.setRegisteredClasses(mockMap);
29 | Deserializer deserializer = new Deserializer();
30 |
31 | Resource resource = deserializer.createObjectFromString("articles");
32 |
33 | assertNotNull(resource);
34 | assertEquals(resource.getClass(), Article.class);
35 | }
36 |
37 | @Test(expected = InstantiationException.class)
38 | public void testCreateObjectFromStringInstantiationException() throws Exception {
39 | HashMap mockMap = mock(HashMap.class);
40 | when(mockMap.get("articles")).thenReturn(InterfaceArticle.class);
41 | Deserializer.setRegisteredClasses(mockMap);
42 | Deserializer deserializer = new Deserializer();
43 |
44 | Resource resource = deserializer.createObjectFromString("articles");
45 | }
46 |
47 | @Test(expected = NotExtendingResourceException.class)
48 | public void testCreateObjectFromStringClassCastException() throws Exception {
49 | HashMap mockMap = mock(HashMap.class);
50 | when(mockMap.get("test")).thenReturn(FalseResource.class);
51 | Deserializer.setRegisteredClasses(mockMap);
52 | Deserializer deserializer = new Deserializer();
53 |
54 | Resource resource = deserializer.createObjectFromString("test");
55 | }
56 |
57 | @Test
58 | public void testSetField() throws Exception {
59 | Deserializer deserializer = new Deserializer();
60 | Article article = new Article();
61 |
62 | Resource resource = deserializer.setField(article, "title", "My Title");
63 |
64 | article = (Article)resource;
65 | assertEquals(article.getTitle(), "My Title");
66 | }
67 |
68 | @Test
69 | public void testSetFieldNoSuchFieldException() throws Exception {
70 | Deserializer deserializer = new Deserializer();
71 | Article article = new Article();
72 |
73 | Resource resource = deserializer.setField(article, "asdf", "My Title");
74 |
75 | article = (Article)resource;
76 | assertNotNull(article);
77 | }
78 |
79 | @Test
80 | public void testSetIdField() throws Exception {
81 | Deserializer deserializer = new Deserializer();
82 | Article article = new Article();
83 |
84 | Resource resource = deserializer.setIdField(article, "123456");
85 |
86 | article = (Article)resource;
87 | assertEquals(article.getId(), "123456");
88 | }
89 |
90 | @Test
91 | public void testSetIdFieldNumber() throws Exception {
92 | Deserializer deserializer = new Deserializer();
93 | Article article = new Article();
94 |
95 | Resource resource = deserializer.setIdField(article, 123456);
96 |
97 | article = (Article)resource;
98 | assertEquals(article.getId(), "123456");
99 | }
100 |
101 | @Test
102 | public void testSetIdFieldMultiExtendingClass() throws Exception {
103 | Deserializer deserializer = new Deserializer();
104 | MultiExtendResource multiExtendResource= new MultiExtendResource();
105 |
106 | Resource resource = deserializer.setIdField(multiExtendResource, 123456);
107 |
108 | multiExtendResource = (MultiExtendResource)resource;
109 | assertEquals(multiExtendResource.getId(), "123456");
110 | }
111 |
112 | }
113 |
--------------------------------------------------------------------------------
/morpheus/src/main/java/at/rags/morpheus/Factory.java:
--------------------------------------------------------------------------------
1 | package at.rags.morpheus;
2 |
3 | import org.json.JSONArray;
4 | import org.json.JSONException;
5 | import org.json.JSONObject;
6 |
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | /**
11 | * Factory to create and map {@link Resource}.
12 | */
13 | public class Factory {
14 |
15 | private static Mapper mapper = new Mapper();
16 | private static Deserializer deserializer = new Deserializer();
17 |
18 | /**
19 | * Deserializes a json object of data to the registered class.
20 | *
21 | * @param dataObject JSONObject from data
22 | * @param included {@literal List} from includes to automatic match them.
23 | * @return Deserialized Object.
24 | * @throws Exception when deserializer is not able to create instance.
25 | */
26 | public static Resource newObjectFromJSONObject(JSONObject dataObject, List included) throws Exception {
27 | Resource realObject = null;
28 |
29 | try {
30 | realObject = deserializer.createObjectFromString(getTypeFromJson(dataObject));
31 | } catch (Exception e) {
32 | throw e;
33 | }
34 |
35 | try {
36 | realObject = mapper.mapId(realObject, dataObject);
37 | } catch (Exception e) {
38 | Logger.debug("JSON data does not contain id");
39 | }
40 |
41 | try {
42 | realObject = mapper.mapAttributes(realObject, dataObject.getJSONObject("attributes"));
43 | } catch (Exception e) {
44 | Logger.debug("JSON data does not contain attributes");
45 | }
46 |
47 | try {
48 | realObject = mapper.mapRelations(realObject, dataObject.getJSONObject("relationships"), included);
49 | } catch (Exception e) {
50 | Logger.debug("JSON data does not contain relationships");
51 | }
52 |
53 | try {
54 | assert realObject != null;
55 | realObject.setMeta(mapper.getAttributeMapper().createMapFromJSONObject(dataObject.getJSONObject("meta")));
56 | } catch (Exception e) {
57 | Logger.debug("JSON data does not contain meta");
58 | }
59 |
60 | try {
61 | realObject.setLinks(mapper.mapLinks(dataObject.getJSONObject("links")));
62 | } catch (JSONException e) {
63 | Logger.debug("JSON data does not contain links");
64 | }
65 |
66 | return realObject;
67 | }
68 |
69 | /**
70 | * Loops through data objects and deserializes them.
71 | *
72 | * @param dataArray JSONArray of the data node.
73 | * @param included {@literal List} from includes to automatic match them.
74 | * @return List of deserialized objects.
75 | * @throws Exception when deserializer is not able to create instance.
76 | */
77 | public static List newObjectFromJSONArray(JSONArray dataArray, List included) throws Exception {
78 | ArrayList objects = new ArrayList<>();
79 |
80 | for (int i = 0; i < dataArray.length(); i++) {
81 | JSONObject jsonObject = null;
82 |
83 | try {
84 | jsonObject = dataArray.getJSONObject(i);
85 | } catch (JSONException e) {
86 | Logger.debug("Was not able to get dataArray["+i+"] as JSONObject.");
87 | }
88 | try {
89 | objects.add(newObjectFromJSONObject(jsonObject, included));
90 | } catch (Exception e) {
91 | throw e;
92 | }
93 | }
94 |
95 | return objects;
96 | }
97 |
98 | // helper
99 |
100 | /**
101 | * Get the type of the data message.
102 | *
103 | * @param object JSONObject.
104 | * @return Name of the json type.
105 | */
106 | public static String getTypeFromJson(JSONObject object) {
107 | String type = null;
108 | try {
109 | type = object.getString("type");
110 | } catch (JSONException e) {
111 | Logger.debug("JSON data does not contain type");
112 | }
113 | return type;
114 | }
115 |
116 | public static void setDeserializer(Deserializer deserializer) {
117 | Factory.deserializer = deserializer;
118 | }
119 |
120 | public static void setMapper(Mapper mapper) {
121 | Factory.mapper = mapper;
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/morpheus/src/main/java/at/rags/morpheus/Deserializer.java:
--------------------------------------------------------------------------------
1 | package at.rags.morpheus;
2 |
3 | import android.util.ArrayMap;
4 |
5 | import java.lang.reflect.Field;
6 | import java.util.HashMap;
7 |
8 | import at.rags.morpheus.Exceptions.NotExtendingResourceException;
9 |
10 | /**
11 | * Deserializer uses reflection to create objects and set fields.
12 | */
13 | public class Deserializer {
14 |
15 | private static HashMap registeredClasses = new HashMap<>();
16 |
17 | /**
18 | * Register your class for a JSON type.
19 | *
20 | * Example:
21 | * registerResourceClass("articles", Article.class);
22 | *
23 | * @param typeName Name of the JSONAPI type.
24 | * @param resourceClass Class for mapping.
25 | * @see Resource
26 | */
27 | public static void registerResourceClass(String typeName, Class resourceClass) {
28 | registeredClasses.put(typeName, resourceClass);
29 | }
30 |
31 | /**
32 | * Creates an instance of an object via its name.
33 | *
34 | * @param resourceName Name of the resource.
35 | * @return Instance of the resourceName class.
36 | * @throws InstantiationException Throws exception when not able to create instance of class.
37 | * @throws IllegalAccessException Throws exception when not able to create instance of class.
38 | * @throws NotExtendingResourceException Throws exception when not able to create instance of class.
39 | */
40 | public Resource createObjectFromString(String resourceName) throws InstantiationException, IllegalAccessException, NotExtendingResourceException {
41 | Class objectClass = registeredClasses.get(resourceName);
42 | try {
43 | return (Resource) objectClass.newInstance();
44 | } catch (InstantiationException e) {
45 | throw e;
46 | } catch(IllegalAccessException e) {
47 | throw e;
48 | } catch (ClassCastException e) {
49 | throw new NotExtendingResourceException(objectClass + " is not inheriting Resource");
50 | }
51 | }
52 |
53 | /**
54 | * Sets the field of the resourceObject with the data.
55 | *
56 | * @param resourceObject Object with field to be set.
57 | * @param fieldName Name of the field.
58 | * @param data Data to set.
59 | * @return Resource with or without field set
60 | */
61 | public Resource setField(Resource resourceObject, String fieldName, Object data) {
62 | Field field = null;
63 | try {
64 | field = resourceObject.getClass().getDeclaredField(fieldName);
65 | field.setAccessible(true);
66 | field.set(resourceObject, data);
67 | } catch (NoSuchFieldException e) {
68 | Logger.debug("Field " + fieldName + " not found.");
69 | } catch (IllegalAccessException e) {
70 | Logger.debug("Could not access " + field.getName() + " field");
71 | }
72 |
73 | return resourceObject;
74 | }
75 |
76 | /**
77 | * Sets the Id field of the resourceObject extending {@link Resource}.
78 | *
79 | * @param resourceObject Object extending {@link Resource}.
80 | * @param data Data with Id (as String or Int)
81 | * @return ResourceObject with set Id as String.
82 | * @throws NotExtendingResourceException when none of the superclasses are {@link Resource}.
83 | */
84 | public Resource setIdField(Resource resourceObject, Object data) throws NotExtendingResourceException {
85 | Class superClass = null;
86 | try {
87 | superClass = getMorpheusResourceSuperClass(resourceObject);
88 | } catch (NotExtendingResourceException e) {
89 | throw e;
90 | }
91 |
92 | try {
93 | Field field = superClass.getDeclaredField("Id");
94 | field.setAccessible(true);
95 | if (data instanceof String) {
96 | field.set(resourceObject, data);
97 | } else {
98 | field.set(resourceObject, String.valueOf(data));
99 | }
100 | } catch (NoSuchFieldException e) {
101 | Logger.debug("No field Id found. That should not happened.");
102 | } catch (IllegalAccessException e) {
103 | Logger.debug("Could not access field Id");
104 | }
105 |
106 | return resourceObject;
107 | }
108 |
109 | /**
110 | * Returns the superclass if instance of {@link Resource}.
111 | *
112 | * @param resourceObject Object to find the superclass.
113 | * @return {@link Resource} class.
114 | * @throws NotExtendingResourceException when resourceObject is not extending {@link Resource}.
115 | */
116 | private Class getMorpheusResourceSuperClass(Resource resourceObject) throws NotExtendingResourceException {
117 | Class superClass = resourceObject.getClass().getSuperclass();
118 | do {
119 | if (superClass == Resource.class) {
120 | break;
121 | }
122 | superClass = superClass.getSuperclass();
123 | } while (superClass != null);
124 |
125 | if (superClass == null) { //should not happen, cause createObjectFromString() checks
126 | throw new NotExtendingResourceException(resourceObject.getClass() + " is not inheriting Resource");
127 | }
128 |
129 | return superClass;
130 | }
131 |
132 | public static HashMap getRegisteredClasses() {
133 | return registeredClasses;
134 | }
135 |
136 | public static void setRegisteredClasses(HashMap registeredClasses) {
137 | Deserializer.registeredClasses = registeredClasses;
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/morpheus/src/main/java/at/rags/morpheus/AttributeMapper.java:
--------------------------------------------------------------------------------
1 | package at.rags.morpheus;
2 |
3 | import com.google.gson.Gson;
4 |
5 | import org.json.JSONArray;
6 | import org.json.JSONException;
7 | import org.json.JSONObject;
8 |
9 | import java.lang.reflect.Field;
10 | import java.lang.reflect.ParameterizedType;
11 | import java.lang.reflect.Type;
12 | import java.util.ArrayList;
13 | import java.util.HashMap;
14 | import java.util.Iterator;
15 | import java.util.List;
16 |
17 | /**
18 | * AttributeMapper is used to map the json:api attribute node to
19 | * your object fields.
20 | *
21 | * You can create your own AttributeMapper and set it via {@link Morpheus#Morpheus(AttributeMapper)}.
22 | */
23 | public class AttributeMapper {
24 | private Deserializer deserializer;
25 | private Gson gson;
26 |
27 | public AttributeMapper() {
28 | deserializer = new Deserializer();
29 | gson = new Gson();
30 | }
31 |
32 | public AttributeMapper(Deserializer deserializer, Gson gson) {
33 | this.deserializer = deserializer;
34 | this.gson = gson;
35 | }
36 |
37 | /**
38 | * Will map the attributes of the JSONAPI attribute object.
39 | * JSONArrays will get mapped as {@literal List}.
40 | * JSONObject will get mapped as {@literal ArrayMap}.
41 | * Everything else will get mapped without changes.
42 | *
43 | * @param jsonApiResource Object extended with {@link Resource} that will get the field set.
44 | * @param attributesJsonObject {@link JSONObject} with json:api attributes object
45 | * @param field Field that will be set.
46 | * @param jsonFieldName Name of the json-field in attributesJsonObject to get data from.
47 | */
48 | public void mapAttributeToObject(Resource jsonApiResource, JSONObject attributesJsonObject,
49 | Field field, String jsonFieldName) {
50 |
51 | Object object = null;
52 | try {
53 | object = attributesJsonObject.get(jsonFieldName);
54 | } catch (JSONException e) {
55 | Logger.debug("JSON attributes does not contain " + jsonFieldName);
56 | return;
57 | }
58 |
59 | if (object instanceof JSONArray) {
60 |
61 | List list = null;
62 | try {
63 | list = createListFromJSONArray(attributesJsonObject.getJSONArray(jsonFieldName), field);
64 | } catch (JSONException e) {
65 | Logger.debug(jsonFieldName + " is not an valid JSONArray.");
66 | }
67 |
68 | deserializer.setField(jsonApiResource, field.getName(), list);
69 |
70 | } else if (object.getClass() == JSONObject.class) {
71 | Object obj = gson.fromJson(object.toString(), field.getType());
72 | deserializer.setField(jsonApiResource, field.getName(), obj);
73 | } else {
74 | deserializer.setField(jsonApiResource, field.getName(), object);
75 | }
76 |
77 | }
78 |
79 | /**
80 | * Will loop through JSONArray and return values as List.
81 | *
82 | * @param jsonArray JSONArray with values.
83 | * @return List of JSONArray values.
84 | */
85 | private List createListFromJSONArray(JSONArray jsonArray, Field field) {
86 | Type genericFieldType = field.getGenericType();
87 | List objectArrayList = new ArrayList<>();
88 |
89 | if(genericFieldType instanceof ParameterizedType) {
90 | ParameterizedType aType = (ParameterizedType) genericFieldType;
91 | Type[] fieldArgTypes = aType.getActualTypeArguments();
92 | for (Type fieldArgType : fieldArgTypes) {
93 | final Class fieldArgClass = (Class) fieldArgType;
94 |
95 | for (int i = 0; jsonArray.length() > i; i++) {
96 | Object obj = null;
97 | Object jsonObject = null;
98 |
99 | try {
100 | jsonObject = jsonArray.get(i);
101 | } catch (JSONException e) {
102 | Logger.debug("JSONArray does not contain index " + i + ".");
103 | continue;
104 | }
105 |
106 | // if this is a String, it wont use gson because it can throw a malformed json exception
107 | // that case happens if there is a String with ":" in it.
108 | if (fieldArgClass == String.class) {
109 | obj = jsonObject.toString();
110 | } else {
111 | try {
112 | obj = gson.fromJson(jsonArray.get(i).toString(), fieldArgClass);
113 | } catch (JSONException e) {
114 | Logger.debug("JSONArray does not contain index " + i + ".");
115 | }
116 | }
117 |
118 | objectArrayList.add(obj);
119 | }
120 | }
121 | }
122 |
123 | return objectArrayList;
124 | }
125 |
126 | /**
127 | * Will loop through JSONObject and return values as map.
128 | *
129 | * @param jsonObject JSONObject for meta.
130 | * @return HashMap with meta values.
131 | */
132 | public HashMap createMapFromJSONObject(JSONObject jsonObject) {
133 | HashMap metaMap = new HashMap<>();
134 |
135 | for(Iterator iter = jsonObject.keys(); iter.hasNext();) {
136 | String key = iter.next();
137 |
138 | try {
139 | metaMap.put(key, jsonObject.get(key));
140 | } catch (JSONException e) {
141 | Logger.debug("JSON does not contain " + key + ".");
142 | }
143 | }
144 |
145 | return metaMap;
146 | }
147 |
148 | }
149 |
--------------------------------------------------------------------------------
/morpheus/src/main/java/at/rags/morpheus/Morpheus.java:
--------------------------------------------------------------------------------
1 | package at.rags.morpheus;
2 |
3 | import com.google.gson.Gson;
4 | import com.google.gson.GsonBuilder;
5 |
6 | import org.json.JSONArray;
7 | import org.json.JSONException;
8 | import org.json.JSONObject;
9 |
10 | import java.util.ArrayList;
11 | import java.util.HashMap;
12 |
13 | /**
14 | * Morpheus is a library to map JSON with the json:api specification format.
15 | * (http://jsonapi.org/).
16 | *
17 | * Feel free to contribute on github. (//TODO insert new link here)
18 | *
19 | * Example
20 | *
21 | * {@code
22 | * Morpheus morpheus = new Morpheus();
23 | * JsonApiObject jsonapiObject = morpheus.parse(YOUR-JSON-STRING);
24 | * }
25 | *
26 | */
27 | public class Morpheus {
28 | private Mapper mapper;
29 |
30 | public Morpheus() {
31 | mapper = new Mapper();
32 | }
33 |
34 | public Morpheus(AttributeMapper attributeMapper) {
35 | mapper = new Mapper(new Deserializer(), new Serializer(), attributeMapper);
36 | Factory.setMapper(mapper);
37 | }
38 |
39 | /**
40 | * Will return you an {@link JsonApiObject} with parsed objects, links, relations and includes.
41 | *
42 | * @param jsonString Your json:api formated string.
43 | * @return A {@link JsonApiObject}.
44 | * @throws JSONException or NotExtendingResourceException
45 | */
46 | public JsonApiObject parse(String jsonString) throws Exception {
47 | JSONObject jsonObject = null;
48 | try {
49 | jsonObject = new JSONObject(jsonString);
50 | } catch (JSONException e) {
51 | throw e;
52 | }
53 |
54 | return parseFromJSONObject(jsonObject);
55 | }
56 |
57 | /**
58 | * Parse and map all the top level members.
59 | */
60 | private JsonApiObject parseFromJSONObject(JSONObject jsonObject) throws Exception {
61 | JsonApiObject jsonApiObject = new JsonApiObject();
62 |
63 | //included
64 | try {
65 | JSONArray includedArray = jsonObject.getJSONArray("included");
66 | jsonApiObject.setIncluded(Factory.newObjectFromJSONArray(includedArray, null));
67 | } catch (JSONException e) {
68 | Logger.debug("JSON does not contain included");
69 | }
70 |
71 | //data array
72 | JSONArray dataArray = null;
73 | try {
74 | dataArray = jsonObject.getJSONArray("data");
75 | jsonApiObject.setResources(Factory.newObjectFromJSONArray(dataArray, jsonApiObject.getIncluded()));
76 | } catch (JSONException e) {
77 | Logger.debug("JSON does not contain data array");
78 | }
79 |
80 | //data object
81 | JSONObject dataObject = null;
82 | try {
83 | dataObject = jsonObject.getJSONObject("data");
84 | jsonApiObject.setResource(Factory.newObjectFromJSONObject(dataObject, jsonApiObject.getIncluded()));
85 | } catch (JSONException e) {
86 | Logger.debug("JSON does not contain data object");
87 | }
88 |
89 | //link object
90 | JSONObject linkObject = null;
91 | try {
92 | linkObject = jsonObject.getJSONObject("links");
93 | jsonApiObject.setLinks(mapper.mapLinks(linkObject));
94 | } catch (JSONException e) {
95 | Logger.debug("JSON does not contain links object");
96 | }
97 |
98 | //meta object
99 | JSONObject metaObject = null;
100 | try {
101 | metaObject = jsonObject.getJSONObject("meta");
102 | jsonApiObject.setMeta(mapper.getAttributeMapper().createMapFromJSONObject(metaObject));
103 | } catch (JSONException e) {
104 | Logger.debug("JSON does not contain meta object");
105 | }
106 |
107 | JSONArray errorArray = null;
108 | try {
109 | errorArray = jsonObject.getJSONArray("errors");
110 | jsonApiObject.setErrors(mapper.mapErrors(errorArray));
111 | } catch (JSONException e) {
112 | Logger.debug("JSON does not contain errors object");
113 | }
114 |
115 | return jsonApiObject;
116 | }
117 |
118 | /**
119 | * Get the serialized json from a JsonApiObject.
120 | * Will parse resource(s) and relationships. If addIncluded is set to true, it will also
121 | * add the relationships as included.
122 | *
123 | * @param jsonApiObject JsonApiObject to serialize.
124 | * @param addIncluded Add includes for relationships.
125 | * @return Json as String.
126 | */
127 | public String createJson(JsonApiObject jsonApiObject, Boolean addIncluded) {
128 | HashMap jsonMap = new HashMap<>();
129 |
130 | ArrayList> included = new ArrayList();
131 |
132 | if (jsonApiObject.getResource() != null) {
133 | HashMap data = mapper.createData(jsonApiObject.getResource(), true);
134 | if (data != null) {
135 | jsonMap.put("data", data);
136 | }
137 |
138 | if (addIncluded) {
139 | included.addAll(mapper.createIncluded(jsonApiObject.getResource()));
140 | }
141 | }
142 |
143 | if (jsonApiObject.getResources() != null) {
144 | ArrayList> data = mapper.createData(jsonApiObject.getResources(), true);
145 | if (data != null) {
146 | jsonMap.put("data", data);
147 | }
148 |
149 | if (addIncluded) {
150 | for (Resource resource : jsonApiObject.getResources()) {
151 | included.addAll(mapper.createIncluded(resource));
152 | }
153 | }
154 | }
155 |
156 | if (addIncluded) {
157 | jsonMap.put("included", included);
158 | }
159 |
160 | Gson gson = new GsonBuilder().serializeNulls().create();
161 | return gson.toJson(jsonMap);
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/morpheus/src/test/java/at/rags/morpheus/AttributeMapperUnitTest.java:
--------------------------------------------------------------------------------
1 | package at.rags.morpheus;
2 |
3 | import android.util.ArrayMap;
4 |
5 | import com.google.gson.Gson;
6 |
7 | import org.json.JSONArray;
8 | import org.json.JSONException;
9 | import org.json.JSONObject;
10 | import org.junit.Before;
11 | import org.junit.Test;
12 | import org.mockito.ArgumentCaptor;
13 | import org.mockito.Matchers;
14 |
15 | import java.lang.reflect.Field;
16 | import java.util.ArrayList;
17 | import java.util.HashMap;
18 | import java.util.Iterator;
19 |
20 | import at.rags.morpheus.TestResources.Article;
21 |
22 | import static org.junit.Assert.assertEquals;
23 | import static org.junit.Assert.assertNotNull;
24 | import static org.junit.Assert.assertTrue;
25 | import static org.mockito.Matchers.anyString;
26 | import static org.mockito.Matchers.eq;
27 | import static org.mockito.Mockito.mock;
28 | import static org.mockito.Mockito.verify;
29 | import static org.mockito.Mockito.when;
30 |
31 | /**
32 | * Created by raphaelseher on 10/03/16.
33 | */
34 | public class AttributeMapperUnitTest {
35 |
36 | private AttributeMapper mAttributeMapper;
37 |
38 | @Before
39 | public void setup() {
40 | mAttributeMapper = new AttributeMapper();
41 | }
42 |
43 | @Test
44 | public void testJsonObjectToArrayMapWithData() throws Exception {
45 | JSONObject jsonObject = mock(JSONObject.class);
46 | Iterator mockIter = mock(Iterator.class);
47 | when(mockIter.hasNext()).thenReturn(true, true, false);
48 | when(mockIter.next()).thenReturn("String 1", "String 2");
49 | when(jsonObject.keys()).thenReturn(mockIter);
50 |
51 | HashMap map = mAttributeMapper.createMapFromJSONObject(jsonObject);
52 |
53 | verify(jsonObject).get(eq("String 1"));
54 | verify(jsonObject).get(eq("String 2"));
55 |
56 | assertNotNull(map);
57 | }
58 |
59 | @Test
60 | public void testmapAttributeToObjectWithString() throws Exception {
61 | JSONObject jsonObject = mock(JSONObject.class);
62 | Deserializer mockDeserializer = mock(Deserializer.class);
63 | AttributeMapper attributeMapper = new AttributeMapper(mockDeserializer, new Gson());
64 | JSONArray jsonArray = mock(JSONArray.class);
65 |
66 | when(jsonObject.get("title")).thenReturn("My title");
67 |
68 | Article article = new Article();
69 | Field field = Article.class.getDeclaredField("title");
70 | attributeMapper.mapAttributeToObject(article, jsonObject, field, "title");
71 |
72 | ArgumentCaptor stringArgumentCaptor = ArgumentCaptor.forClass(String.class);
73 |
74 | verify(mockDeserializer).setField(Matchers.anyObject(), eq("title"), stringArgumentCaptor.capture());
75 | }
76 |
77 | @Test
78 | public void testmapAttributeToObjectJSONException() throws Exception {
79 | JSONObject jsonObject = mock(JSONObject.class);
80 | Deserializer mockDeserializer = mock(Deserializer.class);
81 | AttributeMapper attributeMapper = new AttributeMapper(mockDeserializer, new Gson());
82 | JSONArray jsonArray = mock(JSONArray.class);
83 |
84 | when(jsonArray.length()).thenReturn(3);
85 | when(jsonArray.get(0)).thenReturn("Tag1");
86 | when(jsonArray.get(1)).thenReturn("Tag2");
87 | when(jsonArray.get(2)).thenThrow(new JSONException(""));
88 | when(jsonObject.get("tags")).thenReturn(new JSONArray());
89 | when(jsonObject.getJSONArray("tags")).thenThrow(new JSONException(""));
90 |
91 | Article article = new Article();
92 | Field field = Article.class.getDeclaredField("tags");
93 | attributeMapper.mapAttributeToObject(article, jsonObject, field, "tags");
94 | }
95 |
96 | @Test
97 | public void testmapAttributeToObjectJSONArray() throws Exception {
98 | JSONObject jsonObject = mock(JSONObject.class);
99 | Deserializer mockDeserializer = mock(Deserializer.class);
100 | AttributeMapper attributeMapper = new AttributeMapper(mockDeserializer, new Gson());
101 | JSONArray jsonArray = mock(JSONArray.class);
102 |
103 | when(jsonArray.length()).thenReturn(3);
104 | when(jsonArray.get(0)).thenReturn("Tag1");
105 | when(jsonArray.get(1)).thenReturn("Tag2");
106 | when(jsonArray.get(2)).thenThrow(new JSONException(""));
107 | when(jsonObject.get("tags")).thenReturn(new JSONArray());
108 | when(jsonObject.getJSONArray("tags")).thenReturn(jsonArray);
109 |
110 | Article article = new Article();
111 | Field field = Article.class.getDeclaredField("tags");
112 | attributeMapper.mapAttributeToObject(article, jsonObject, field, "tags");
113 |
114 | ArgumentCaptor listArgumentCaptor = ArgumentCaptor.forClass(ArrayList.class);
115 |
116 | verify(mockDeserializer).setField(Matchers.anyObject(), eq("tags"), listArgumentCaptor.capture());
117 | assertTrue(listArgumentCaptor.getValue().get(0).equals("Tag1"));
118 | assertTrue(listArgumentCaptor.getValue().get(1).equals("Tag2"));
119 | }
120 |
121 | @Test
122 | public void testmapAttributeToObjectJSONObject() throws Exception {
123 | JSONObject jsonObject = mock(JSONObject.class);
124 | Deserializer mockDeserializer = mock(Deserializer.class);
125 | AttributeMapper attributeMapper = new AttributeMapper(mockDeserializer, new Gson());
126 | JSONObject mockJSONObject = mock(JSONObject.class);
127 | Iterator mockIter = mock(Iterator.class);
128 |
129 | when(jsonObject.get("map")).thenReturn(new JSONObject());
130 | when(jsonObject.getJSONObject("map")).thenReturn(mockJSONObject);
131 |
132 | when(mockIter.hasNext()).thenReturn(true, true, false);
133 | when(mockIter.next()).thenReturn("Key 1", "Key 2");
134 | when(mockJSONObject.keys()).thenReturn(mockIter);
135 | when(mockJSONObject.get(anyString())).thenReturn("String");
136 |
137 | Article article = new Article();
138 | Field field = Article.class.getDeclaredField("map");
139 | attributeMapper.mapAttributeToObject(article, jsonObject, field, "map");
140 |
141 | ArgumentCaptor mapArgumentCaptor = ArgumentCaptor.forClass(ArrayMap.class);
142 |
143 | verify(mockDeserializer).setField(Matchers.anyObject(), eq("map"), mapArgumentCaptor.capture());
144 |
145 | assertNotNull(mapArgumentCaptor);
146 | }
147 |
148 | @Test
149 | public void testJsonObjectToArrayMapException() throws Exception {
150 | JSONObject jsonObject = mock(JSONObject.class);
151 | Iterator mockIter = mock(Iterator.class);
152 |
153 | when(mockIter.hasNext()).thenReturn(true, true, false);
154 | when(mockIter.next()).thenReturn("String 1", "String 2");
155 | when(jsonObject.keys()).thenReturn(mockIter);
156 | when(jsonObject.get(anyString())).thenThrow(new JSONException(""));
157 |
158 | HashMap map = mAttributeMapper.createMapFromJSONObject(jsonObject);
159 |
160 | assertNotNull(map);
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | generateDebugSources
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/morpheus/morpheus.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | generateDebugSources
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | <<<<<<< Updated upstream
79 | =======
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | >>>>>>> Stashed changes
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/.idea/codeStyleSettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 | xmlns:android
82 | ^$
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 | xmlns:.*
92 | ^$
93 |
94 |
95 | BY_NAME
96 |
97 |
98 |
99 |
100 |
101 |
102 | .*:id
103 | http://schemas.android.com/apk/res/android
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 | .*:name
113 | http://schemas.android.com/apk/res/android
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 | name
123 | ^$
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 | style
133 | ^$
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 | .*
143 | ^$
144 |
145 |
146 | BY_NAME
147 |
148 |
149 |
150 |
151 |
152 |
153 | .*:layout_width
154 | http://schemas.android.com/apk/res/android
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 | .*:layout_height
164 | http://schemas.android.com/apk/res/android
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 | .*:layout_.*
174 | http://schemas.android.com/apk/res/android
175 |
176 |
177 | BY_NAME
178 |
179 |
180 |
181 |
182 |
183 |
184 | .*:width
185 | http://schemas.android.com/apk/res/android
186 |
187 |
188 | BY_NAME
189 |
190 |
191 |
192 |
193 |
194 |
195 | .*:height
196 | http://schemas.android.com/apk/res/android
197 |
198 |
199 | BY_NAME
200 |
201 |
202 |
203 |
204 |
205 |
206 | .*
207 | http://schemas.android.com/apk/res/android
208 |
209 |
210 | BY_NAME
211 |
212 |
213 |
214 |
215 |
216 |
217 | .*
218 | .*
219 |
220 |
221 | BY_NAME
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
--------------------------------------------------------------------------------
/morpheus/src/androidTest/java/at/rags/morpheus/MorpheusMappingTests.java:
--------------------------------------------------------------------------------
1 | package at.rags.morpheus;
2 |
3 | import android.test.InstrumentationTestCase;
4 |
5 | import org.junit.Test;
6 | import org.junit.runner.RunWith;
7 | import org.junit.runners.JUnit4;
8 |
9 | import java.io.IOException;
10 | import java.io.InputStream;
11 | import java.util.ArrayList;
12 | import java.util.HashMap;
13 | import java.util.List;
14 |
15 | import at.rags.morpheus.Resources.Article;
16 | import at.rags.morpheus.Resources.Author;
17 | import at.rags.morpheus.Resources.Comment;
18 | import at.rags.morpheus.Resources.Location;
19 | import at.rags.morpheus.Resources.Product;
20 |
21 | @RunWith(JUnit4.class)
22 | public class MorpheusMappingTests extends InstrumentationTestCase {
23 |
24 | @Test
25 | public void testInit() throws Exception {
26 | Morpheus morpheus = new Morpheus();
27 | Logger.setDebug(true);
28 | assertNotNull(morpheus);
29 | }
30 |
31 | @Test
32 | public void testDataArray() throws Exception {
33 | Morpheus morpheus = new Morpheus();
34 | Deserializer.registerResourceClass("articles", Article.class);
35 | Deserializer.registerResourceClass("people", Author.class);
36 | Deserializer.registerResourceClass("comments", Comment.class);
37 |
38 | JsonApiObject jsonApiObject =
39 | morpheus.parse(loadJSONFromAsset(R.raw.articles));
40 |
41 | assertTrue(jsonApiObject.getResources().size() == 1);
42 | assertTrue(jsonApiObject.getResources().get(0).getClass() == Article.class);
43 | Article article = (Article) jsonApiObject.getResources().get(0);
44 | assertTrue(article.getId().equals("1"));
45 | assertTrue(article.getTitle().equals("JSON API paints my bikeshed!"));
46 | }
47 |
48 | @Test
49 | public void testDataObject() throws Exception {
50 | Morpheus morpheus = new Morpheus();
51 | Deserializer.registerResourceClass("articles", Article.class);
52 | Deserializer.registerResourceClass("people", Author.class);
53 | Deserializer.registerResourceClass("comments", Comment.class);
54 |
55 | JsonApiObject jsonApiObject =
56 | morpheus.parse(loadJSONFromAsset(R.raw.article));
57 |
58 | assertNotNull(jsonApiObject.getResource());
59 | assertTrue(jsonApiObject.getResource().getClass() == Article.class);
60 | Article article = (Article) jsonApiObject.getResource();
61 | assertTrue(article.getId().equals("1"));
62 | assertTrue(article.getTitle().equals("JSON API paints my bikeshed!"));
63 | }
64 |
65 | @Test
66 | public void testDataObjectMeta() throws Exception {
67 | Morpheus morpheus = new Morpheus();
68 | Deserializer.registerResourceClass("articles", Article.class);
69 | Deserializer.registerResourceClass("people", Author.class);
70 | Deserializer.registerResourceClass("comments", Comment.class);
71 |
72 | JsonApiObject jsonApiObject =
73 | morpheus.parse(loadJSONFromAsset(R.raw.article));
74 |
75 | assertNotNull(jsonApiObject.getResource());
76 | assertTrue(jsonApiObject.getResource().getClass() == Article.class);
77 | Article article = (Article) jsonApiObject.getResource();
78 | assertTrue(article.getId().equals("1"));
79 | assertTrue(article.getTitle().equals("JSON API paints my bikeshed!"));
80 | assertNotNull(article.getMeta());
81 | assertEquals(article.getMeta().get("test-meta"), "yes");
82 | }
83 |
84 |
85 | @Test
86 | public void testRelationship() throws Exception {
87 | Morpheus morpheus = new Morpheus();
88 | Deserializer.registerResourceClass("articles", Article.class);
89 | Deserializer.registerResourceClass("people", Author.class);
90 | Deserializer.registerResourceClass("comments", Comment.class);
91 |
92 | JsonApiObject jsonApiObject =
93 | morpheus.parse(loadJSONFromAsset(R.raw.articles));
94 |
95 | assertTrue(jsonApiObject.getResources().size() == 1);
96 | assertTrue(jsonApiObject.getResources().get(0).getClass() == Article.class);
97 | Article article = (Article) jsonApiObject.getResources().get(0);
98 | assertNotNull(article.getAuthor());
99 | assertTrue(article.getComments().size() == 2);
100 | }
101 |
102 | @Test
103 | public void testIncluded() throws Exception {
104 | Morpheus morpheus = new Morpheus();
105 | Deserializer.registerResourceClass("articles", Article.class);
106 | Deserializer.registerResourceClass("people", Author.class);
107 | Deserializer.registerResourceClass("comments", Comment.class);
108 |
109 | JsonApiObject jsonApiObject =
110 | morpheus.parse(loadJSONFromAsset(R.raw.articles));
111 |
112 | assertTrue(jsonApiObject.getIncluded().size() == 3);
113 | }
114 |
115 | @Test
116 | public void testIncludedRelations() throws Exception {
117 | Morpheus morpheus = new Morpheus();
118 | Deserializer.registerResourceClass("articles", Article.class);
119 | Deserializer.registerResourceClass("people", Author.class);
120 | Deserializer.registerResourceClass("comments", Comment.class);
121 |
122 | JsonApiObject jsonApiObject =
123 | morpheus.parse(loadJSONFromAsset(R.raw.article));
124 |
125 | assertNotNull(jsonApiObject.getResource());
126 | Article article = (Article) jsonApiObject.getResource();
127 | assertTrue(article.getAuthor().getFirstName().equals("Dan"));
128 |
129 | Comment comment = (Comment)article.getComments().get(0);
130 | assertTrue(comment.getBody().equals("First!"));
131 | }
132 |
133 | @Test
134 | public void testLinks() throws Exception {
135 | Morpheus morpheus = new Morpheus();
136 | Deserializer.registerResourceClass("articles", Article.class);
137 | Deserializer.registerResourceClass("people", Author.class);
138 | Deserializer.registerResourceClass("comments", Comment.class);
139 |
140 | JsonApiObject jsonApiObject =
141 | morpheus.parse(loadJSONFromAsset(R.raw.article));
142 |
143 | assertEquals(jsonApiObject.getLinks().getSelfLink(), "http://example.com/articles");
144 | assertEquals(jsonApiObject.getLinks().getNext(), "http://example.com/articles?page[offset]=2");
145 | assertEquals(jsonApiObject.getLinks().getLast(), "http://example.com/articles?page[offset]=10");
146 |
147 | assertNotNull(jsonApiObject.getResource());
148 | Article article = (Article) jsonApiObject.getResource();
149 | assertNotNull(article.getLinks());
150 | assertTrue(article.getLinks().getSelfLink().equals("http://example.com/articles/1"));
151 | assertNull(article.getLinks().getRelated());
152 | }
153 |
154 | @Test
155 | public void testPaginationLinks() throws Exception {
156 | Morpheus morpheus = new Morpheus();
157 | Deserializer.registerResourceClass("articles", Article.class);
158 | Deserializer.registerResourceClass("people", Author.class);
159 | Deserializer.registerResourceClass("comments", Comment.class);
160 |
161 | JsonApiObject jsonApiObject =
162 | morpheus.parse(loadJSONFromAsset(R.raw.article));
163 |
164 | assertNotNull(jsonApiObject.getLinks());
165 | assertTrue(jsonApiObject.getLinks().getSelfLink().equals("http://example.com/articles"));
166 | assertTrue(jsonApiObject.getLinks().getNext().equals("http://example.com/articles?page[offset]=2"));
167 | assertTrue(jsonApiObject.getLinks().getLast().equals("http://example.com/articles?page[offset]=10"));
168 | assertNull(jsonApiObject.getLinks().getRelated());
169 | }
170 |
171 | @Test
172 | public void testMeta() throws Exception {
173 | Morpheus morpheus = new Morpheus();
174 | Deserializer.registerResourceClass("articles", Article.class);
175 | Deserializer.registerResourceClass("people", Author.class);
176 | Deserializer.registerResourceClass("comments", Comment.class);
177 |
178 | JsonApiObject jsonApiObject =
179 | morpheus.parse(loadJSONFromAsset(R.raw.articles));
180 |
181 | assertNotNull(jsonApiObject.getMeta());
182 | assertTrue(jsonApiObject.getMeta().get("testmeta").equals("yes"));
183 | }
184 |
185 | @Test
186 | public void testAttributesArray() throws Exception {
187 | Morpheus morpheus = new Morpheus();
188 | Deserializer.registerResourceClass("articles", Article.class);
189 | Deserializer.registerResourceClass("people", Author.class);
190 | Deserializer.registerResourceClass("comments", Comment.class);
191 |
192 | JsonApiObject jsonApiObject =
193 | morpheus.parse(loadJSONFromAsset(R.raw.article));
194 |
195 | assertNotNull(jsonApiObject.getLinks());
196 | Article article = (Article) jsonApiObject.getResource();
197 |
198 | assertTrue(article.getTags().get(0).equals("main"));
199 | assertTrue(article.getTags().get(1).equals("dev"));
200 | }
201 |
202 | @Test
203 | public void testAttributesTypes() throws Exception {
204 | Morpheus morpheus = new Morpheus();
205 | Deserializer.registerResourceClass("products", Product.class);
206 |
207 | JsonApiObject jsonApiObject =
208 | morpheus.parse(loadJSONFromAsset(R.raw.product));
209 |
210 | Product product = (Product) jsonApiObject.getResources().get(0);
211 |
212 | assertTrue(product.getId().equals("123456"));
213 | assertTrue(product.getName().equals("Fancy new roboter"));
214 | assertTrue(product.getPrice() == 999.75);
215 | assertTrue(product.getInStock() == 9);
216 | assertTrue(product.getAvailability().get("Store 1"));
217 | assertFalse(product.getAvailability().get("Store 3"));
218 | assertEquals(product.getLocation().getLat(), 14.202323);
219 | assertEquals(product.getLocation().getLon(), 12.04995);
220 | assertEquals(product.getAuthors().size(), 1);
221 | assertEquals(product.getAuthors().get(0).getClass(), Author.class);
222 | assertEquals(product.getAuthors().get(0).getFirstName(), "raphael");
223 | assertEquals(product.getTimes().get(0), "9:14");
224 | assertEquals(product.getTimes().get(1), "12 15");
225 |
226 | }
227 |
228 | @Test
229 | public void testErrors() throws Exception {
230 | Morpheus morpheus = new Morpheus();
231 | Deserializer.registerResourceClass("products", Product.class);
232 |
233 | JsonApiObject jsonApiObject =
234 | morpheus.parse(loadJSONFromAsset(R.raw.error));
235 |
236 | assertNotNull(jsonApiObject.getErrors());
237 | assertEquals(jsonApiObject.getErrors().get(0).getStatus(), "403");
238 | assertEquals(jsonApiObject.getErrors().get(0).getSource().getPointer(), "/data/attributes/secret-powers");
239 | assertEquals(jsonApiObject.getErrors().get(0).getDetail(), "Editing secret powers is not authorized on Sundays.");
240 |
241 | assertEquals(jsonApiObject.getErrors().get(1).getStatus(), "422");
242 | assertEquals(jsonApiObject.getErrors().get(1).getId(), "1");
243 | assertEquals(jsonApiObject.getErrors().get(1).getCode(), "2");
244 | assertEquals(jsonApiObject.getErrors().get(1).getSource().getPointer(), "/data/attributes/volume");
245 | assertEquals(jsonApiObject.getErrors().get(1).getSource().getParameter(), "/data/attributes/battery");
246 | assertEquals(jsonApiObject.getErrors().get(1).getTitle(), "some title");
247 | assertEquals(jsonApiObject.getErrors().get(1).getDetail(), "Volume does not, in fact, go to 11.");
248 | assertEquals(jsonApiObject.getErrors().get(1).getLinks().getAbout(), "about.com");
249 |
250 | assertEquals(jsonApiObject.getErrors().get(2).getStatus(), "500");
251 | assertEquals(jsonApiObject.getErrors().get(2).getSource().getPointer(), "/data/attributes/reputation");
252 | assertNull(jsonApiObject.getErrors().get(2).getSource().getParameter());
253 | assertEquals(jsonApiObject.getErrors().get(2).getTitle(), "The backend responded with an error");
254 | assertEquals(jsonApiObject.getErrors().get(2).getDetail(), "Reputation service not responding after three requests.");
255 | }
256 |
257 | @Test
258 | public void testCreateJsonWithResourceRelationsIncluded() {
259 | Morpheus morpheus = new Morpheus();
260 | Deserializer.registerResourceClass("articles", Article.class);
261 | Deserializer.registerResourceClass("people", Author.class);
262 | Deserializer.registerResourceClass("comments", Comment.class);
263 |
264 | String checkJson = "{\"included\":[{\"attributes\":{\"body\":\"body\"},\"id\":\"3\",\"type\":\"comments\"},{\"attributes\":{\"body\":\"body\"},\"id\":\"3\",\"type\":\"comments\"},{\"attributes\":{\"first-name\":\"Peter\"},\"id\":\"2\",\"type\":\"people\"}],\"data\":{\"attributes\":{\"title\":\"Some title\"},\"id\":\"1\",\"type\":\"articles\",\"relationships\":{\"comments\":{\"data\":[{\"id\":\"3\",\"type\":\"comments\"},{\"id\":\"3\",\"type\":\"comments\"}]},\"author\":{\"data\":{\"id\":\"2\",\"type\":\"people\"}}}}}";
265 |
266 | Article article = new Article();
267 | article.setId("1");
268 | article.setTitle("Some title");
269 |
270 | Author author = new Author();
271 | author.setId("2");
272 | author.setFirstName("Peter");
273 | article.setAuthor(author);
274 |
275 | Comment comment = new Comment();
276 | comment.setId("3");
277 | comment.setBody("body");
278 |
279 | ArrayList comments = new ArrayList<>();
280 | comments.add(comment);
281 | comments.add(comment);
282 | article.setComments(comments);
283 |
284 | JsonApiObject jsonApiObject = new JsonApiObject();
285 | jsonApiObject.setResource(article);
286 |
287 |
288 | String json = morpheus.createJson(jsonApiObject, true);
289 |
290 |
291 | assertEquals(json, checkJson);
292 | }
293 |
294 | @Test
295 | public void testCreateJsonWithResourcesRelations() {
296 | Morpheus morpheus = new Morpheus();
297 | Deserializer.registerResourceClass("articles", Article.class);
298 | Deserializer.registerResourceClass("people", Author.class);
299 | Deserializer.registerResourceClass("comments", Comment.class);
300 |
301 | String checkJson = "{\"data\":[{\"attributes\":{\"title\":\"Some title\"},\"id\":\"1\",\"type\":\"articles\"},{\"attributes\":{\"title\":\"Some title\"},\"id\":\"1\",\"type\":\"articles\"}]}";
302 |
303 | Article article = new Article();
304 | article.setId("1");
305 | article.setTitle("Some title");
306 |
307 | ArrayList articles = new ArrayList<>();
308 | articles.add(article);
309 | articles.add(article);
310 |
311 | JsonApiObject jsonApiObject = new JsonApiObject();
312 | jsonApiObject.setResources(articles);
313 |
314 |
315 | String json = morpheus.createJson(jsonApiObject, false);
316 |
317 |
318 | assertEquals(json, checkJson);
319 | }
320 |
321 | @Test
322 | public void testCreateJsonAttributes() {
323 | Morpheus morpheus = new Morpheus();
324 | Deserializer.registerResourceClass("products", Product.class);
325 |
326 | String checkJson = "{\"data\":{\"attributes\":{\"stores-availability\":{\"there\":false,\"here\":true},\"price\":10.3,\"in-stock\":10,\"location\":{\"lat\":10.3,\"lon\":9.7},\"product-name\":\"robot\",\"categories\":[\"one\",\"two\"]},\"id\":\"10203\",\"type\":\"products\"}}";
327 |
328 | List categories = new ArrayList<>();
329 | categories.add("one");
330 | categories.add("two");
331 |
332 | HashMap availability = new HashMap<>();
333 | availability.put("here", true);
334 | availability.put("there", false);
335 |
336 | Location location = new Location();
337 | location.setLat(10.3);
338 | location.setLon(9.7);
339 |
340 | Product product = new Product();
341 | product.setId("10203");
342 | product.setName("robot");
343 | product.setCategories(categories);
344 | product.setPrice(10.3);
345 | product.setInStock(10);
346 | product.setAvailability(availability);
347 | product.setLocation(location);
348 |
349 | JsonApiObject jsonApiObject = new JsonApiObject();
350 | jsonApiObject.setResource(product);
351 |
352 |
353 | String json = morpheus.createJson(jsonApiObject, false);
354 |
355 |
356 | assertEquals(json, checkJson);
357 | }
358 |
359 | @Test
360 | public void testCreateJsonWithResourceRelationsNullify() {
361 | Morpheus morpheus = new Morpheus();
362 | Deserializer.registerResourceClass("articles", Article.class);
363 | Deserializer.registerResourceClass("people", Author.class);
364 | Deserializer.registerResourceClass("comments", Comment.class);
365 |
366 | String checkJson = "{\"data\":{\"attributes\":{\"title\":\"Some title\"},\"id\":\"1\",\"type\":\"articles\",\"relationships\":{\"comments\":{\"data\":[]},\"author\":{\"data\":null}}}}";
367 |
368 | Article article = new Article();
369 | article.setId("1");
370 | article.setTitle("Some title");
371 |
372 | Author author = new Author();
373 | author.setId("2");
374 | author.setFirstName("Peter");
375 | article.setAuthor(author);
376 |
377 | Comment comment = new Comment();
378 | comment.setId("3");
379 | comment.setBody("body");
380 |
381 | ArrayList comments = new ArrayList<>();
382 | comments.add(comment);
383 | comments.add(comment);
384 | article.setComments(comments);
385 |
386 | article.addRelationshipToNull("author");
387 | article.addRelationshipToNull("comments");
388 |
389 | JsonApiObject jsonApiObject = new JsonApiObject();
390 | jsonApiObject.setResource(article);
391 |
392 |
393 | String json = morpheus.createJson(jsonApiObject, false);
394 |
395 |
396 | assertEquals(json, checkJson);
397 | }
398 |
399 | // helper
400 |
401 | private String loadJSONFromAsset(int file) {
402 | String json = null;
403 | try {
404 | InputStream is = getInstrumentation().getContext().getResources().openRawResource(file);
405 | int size = is.available();
406 | byte[] buffer = new byte[size];
407 | is.read(buffer);
408 | is.close();
409 | json = new String(buffer, "UTF-8");
410 | } catch (IOException ex) {
411 | fail("Was not able to load raw resource: " + file);
412 | }
413 | return json;
414 | }
415 | }
--------------------------------------------------------------------------------
/morpheus/src/main/java/at/rags/morpheus/Mapper.java:
--------------------------------------------------------------------------------
1 | package at.rags.morpheus;
2 |
3 | import com.google.gson.annotations.SerializedName;
4 |
5 | import org.json.JSONArray;
6 | import org.json.JSONException;
7 | import org.json.JSONObject;
8 |
9 | import java.lang.annotation.Annotation;
10 | import java.lang.reflect.Field;
11 | import java.util.ArrayList;
12 | import java.util.HashMap;
13 | import java.util.List;
14 |
15 | import at.rags.morpheus.Annotations.Relationship;
16 | import at.rags.morpheus.Exceptions.NotExtendingResourceException;
17 |
18 | /**
19 | * Mapper will map all different top-level members and will
20 | * also map the relations.
21 | *
22 | * Includes will also mapped to matching relationship members.
23 | */
24 | public class Mapper {
25 |
26 | private Deserializer deserializer;
27 | private Serializer serializer;
28 | private AttributeMapper attributeMapper;
29 |
30 | public Mapper() {
31 | deserializer = new Deserializer();
32 | serializer = new Serializer();
33 | attributeMapper = new AttributeMapper();
34 | }
35 |
36 | public Mapper(Deserializer deserializer, Serializer serializer, AttributeMapper attributeMapper) {
37 | this.deserializer = deserializer;
38 | this.serializer = serializer;
39 | this.attributeMapper = attributeMapper;
40 | }
41 |
42 | //TODO map href and meta (http://jsonapi.org/format/#document-links)
43 | /**
44 | * Will map links and return them.
45 | *
46 | * @param linksJsonObject JSONObject from link.
47 | * @return Links with mapped values.
48 | */
49 | public Links mapLinks(JSONObject linksJsonObject) {
50 | Links links = new Links();
51 | try {
52 | links.setSelfLink(linksJsonObject.getString("self"));
53 | } catch (JSONException e) {
54 | Logger.debug("JSON link does not contain self");
55 | }
56 |
57 | try {
58 | links.setRelated(linksJsonObject.getString("related"));
59 | } catch (JSONException e) {
60 | Logger.debug("JSON link does not contain related");
61 | }
62 |
63 | try {
64 | links.setFirst(linksJsonObject.getString("first"));
65 | } catch (JSONException e) {
66 | Logger.debug("JSON link does not contain first");
67 | }
68 |
69 | try {
70 | links.setLast(linksJsonObject.getString("last"));
71 | } catch (JSONException e) {
72 | Logger.debug("JSON link does not contain last");
73 | }
74 |
75 | try {
76 | links.setPrev(linksJsonObject.getString("prev"));
77 | } catch (JSONException e) {
78 | Logger.debug("JSON link does not contain prev");
79 | }
80 |
81 | try {
82 | links.setNext(linksJsonObject.getString("next"));
83 | } catch (JSONException e) {
84 | Logger.debug("JSON link does not contain next");
85 | }
86 |
87 | return links;
88 | }
89 |
90 | /**
91 | * Map the Id from json to the object.
92 | *
93 | * @param object Object of the class.
94 | * @param jsonDataObject JSONObject of the dataNode.
95 | * @return Object with mapped fields.
96 | * @throws NotExtendingResourceException Throws when the object is not extending {@link Resource}
97 | */
98 | public Resource mapId(Resource object, JSONObject jsonDataObject) throws NotExtendingResourceException {
99 | try {
100 | return deserializer.setIdField(object, jsonDataObject.get("id"));
101 | } catch (JSONException e) {
102 | Logger.debug("JSON data does not contain id.");
103 | }
104 |
105 | return object;
106 | }
107 |
108 | /**
109 | * Maps the attributes of json to the object.
110 | *
111 | * @param object Object of the class.
112 | * @param attributesJsonObject Attributes object inside the data node.
113 | * @return Object with mapped fields.
114 | */
115 | public Resource mapAttributes(Resource object, JSONObject attributesJsonObject) {
116 | if (attributesJsonObject == null) {
117 | return object;
118 | }
119 |
120 | for (Field field : object.getClass().getDeclaredFields()) {
121 | // get the right attribute name
122 | String jsonFieldName = field.getName();
123 | boolean isRelation = false;
124 | for (Annotation annotation : field.getAnnotations()) {
125 | if (annotation.annotationType() == SerializedName.class) {
126 | SerializedName serializeName = (SerializedName) annotation;
127 | jsonFieldName = serializeName.value();
128 | }
129 | if (annotation.annotationType() == Relationship.class) {
130 | isRelation = true;
131 | }
132 | }
133 |
134 | if (isRelation) {
135 | continue;
136 | }
137 |
138 | attributeMapper.mapAttributeToObject(object, attributesJsonObject, field, jsonFieldName);
139 | }
140 |
141 | return object;
142 | }
143 |
144 | /**
145 | * Loops through relation JSON array and maps annotated objects.
146 | *
147 | * @param object Real object to map.
148 | * @param jsonObject JSONObject.
149 | * @param included List of included resources.
150 | * @return Real object with relations.
151 | * @throws Exception when deserializer is not able to create instance.
152 | */
153 | public Resource mapRelations(Resource object, JSONObject jsonObject,
154 | List included) throws Exception {
155 | HashMap relationshipNames = getRelationshipNames(object.getClass());
156 |
157 | //going through relationship names annotated in Class
158 | for (String relationship : relationshipNames.keySet()) {
159 | JSONObject relationJsonObject = null;
160 | try {
161 | relationJsonObject = jsonObject.getJSONObject(relationship);
162 | } catch (JSONException e) {
163 | Logger.debug("Relationship named " + relationship + "not found in JSON");
164 | continue;
165 | }
166 |
167 | //map json object of data
168 | JSONObject relationDataObject = null;
169 | try {
170 | relationDataObject = relationJsonObject.getJSONObject("data");
171 | Resource relationObject = Factory.newObjectFromJSONObject(relationDataObject, null);
172 |
173 | relationObject = matchIncludedToRelation(relationObject, included);
174 |
175 | deserializer.setField(object, relationshipNames.get(relationship), relationObject);
176 | } catch (JSONException e) {
177 | Logger.debug("JSON relationship does not contain data");
178 | }
179 |
180 | //map json array of data
181 | JSONArray relationDataArray = null;
182 | try {
183 | relationDataArray = relationJsonObject.getJSONArray("data");
184 | List relationArray = Factory.newObjectFromJSONArray(relationDataArray, null);
185 |
186 | relationArray = matchIncludedToRelation(relationArray, included);
187 |
188 | deserializer.setField(object, relationshipNames.get(relationship), relationArray);
189 | } catch (JSONException e) {
190 | Logger.debug("JSON relationship does not contain data");
191 | }
192 | }
193 |
194 | return object;
195 | }
196 |
197 |
198 | /**
199 | * Will check if the relation is included. If true included object will be returned.
200 | *
201 | * @param object Relation resources.
202 | * @param included List of included resources.
203 | * @return Relation of included resource.
204 | */
205 | public Resource matchIncludedToRelation(Resource object, List included) {
206 | if (included == null) {
207 | return object;
208 | }
209 |
210 | for (Resource resource : included) {
211 | if (object.getId().equals(resource.getId()) && object.getClass().equals(resource.getClass())) {
212 | return resource;
213 | }
214 | }
215 | return object;
216 | }
217 |
218 | /**
219 | * Loops through relations and calls {@link #matchIncludedToRelation(Resource, List)}.
220 | *
221 | * @param relationResources List of relation resources.
222 | * @param included List of included resources.
223 | * @return List of relations and/or included resources.
224 | */
225 | public List matchIncludedToRelation(List relationResources, List included) {
226 | List matchedResources = new ArrayList<>();
227 | for (Resource resource : relationResources) {
228 | matchedResources.add(matchIncludedToRelation(resource, included));
229 | }
230 | return matchedResources;
231 | }
232 |
233 | public List mapErrors(JSONArray errorArray) {
234 | List errors = new ArrayList<>();
235 |
236 | for (int i = 0; errorArray.length() > i; i++) {
237 | JSONObject errorJsonObject;
238 | try {
239 | errorJsonObject = errorArray.getJSONObject(i);
240 | } catch (JSONException e) {
241 | Logger.debug("No index " + i + " in error json array");
242 | continue;
243 | }
244 | Error error = new Error();
245 |
246 | try {
247 | error.setId(errorJsonObject.getString("id"));
248 | } catch (JSONException e) {
249 | Logger.debug("JSON object does not contain id");
250 | }
251 |
252 | try {
253 | error.setStatus(errorJsonObject.getString("status"));
254 | } catch (JSONException e) {
255 | Logger.debug("JSON object does not contain status");
256 | }
257 |
258 | try {
259 | error.setCode(errorJsonObject.getString("code"));
260 | } catch (JSONException e) {
261 | Logger.debug("JSON object does not contain code");
262 | }
263 |
264 | try {
265 | error.setTitle(errorJsonObject.getString("title"));
266 | } catch (JSONException e) {
267 | Logger.debug("JSON object does not contain title");
268 | }
269 |
270 | try {
271 | error.setDetail(errorJsonObject.getString("detail"));
272 | } catch (JSONException e) {
273 | Logger.debug("JSON object does not contain detail");
274 | }
275 |
276 | JSONObject sourceJsonObject = null;
277 | try {
278 | sourceJsonObject = errorJsonObject.getJSONObject("source");
279 | }
280 | catch (JSONException e) {
281 | Logger.debug("JSON object does not contain source");
282 | }
283 |
284 | if (sourceJsonObject != null) {
285 | Source source = new Source();
286 | try {
287 | source.setParameter(sourceJsonObject.getString("parameter"));
288 | } catch (JSONException e) {
289 | Logger.debug("JSON object does not contain parameter");
290 | }
291 | try {
292 | source.setPointer(sourceJsonObject.getString("pointer"));
293 | } catch (JSONException e) {
294 | Logger.debug("JSON object does not contain pointer");
295 | }
296 | error.setSource(source);
297 | }
298 |
299 | try {
300 | JSONObject linksJsonObject = errorJsonObject.getJSONObject("links");
301 | ErrorLinks links = new ErrorLinks();
302 | links.setAbout(linksJsonObject.getString("about"));
303 | error.setLinks(links);
304 | }
305 | catch (JSONException e) {
306 | Logger.debug("JSON object does not contain links or about");
307 | }
308 |
309 | try {
310 | error.setMeta(attributeMapper.createMapFromJSONObject(errorJsonObject.getJSONObject("meta")));
311 | } catch (JSONException e) {
312 | Logger.debug("JSON object does not contain JSONObject meta");
313 | }
314 |
315 | errors.add(error);
316 | }
317 |
318 | return errors;
319 | }
320 |
321 | /**
322 | * Create data representation from resources.
323 | * This will return the representation of the resources as list of maps. Every item contains
324 | * a map with the resource's id, type and relationships (if any). Attributes are only included
325 | * when 'includeAttributes' is true.
326 | *
327 | * @param resources List of resources.
328 | * @param includeAttributes Add attributes map to representation.
329 | * @return ArrayList of Hashmaps.
330 | */
331 | public ArrayList> createData(List resources,
332 | boolean includeAttributes) {
333 | String resourceName = null;
334 | try {
335 | resourceName = nameForResourceClass(resources.get(0).getClass());
336 | } catch (Exception e) {
337 | Logger.debug(e.getMessage());
338 | return null;
339 | }
340 |
341 | ArrayList> dataArray = new ArrayList<>();
342 |
343 | for (Resource resource : resources) {
344 | HashMap attributes = serializer.getFieldsAsDictionary(resource);
345 |
346 | HashMap resourceRepresentation = new HashMap<>();
347 | resourceRepresentation.put("type", resourceName);
348 | resourceRepresentation.put("id", resource.getId());
349 | if (includeAttributes) {
350 | resourceRepresentation.put("attributes", attributes);
351 | }
352 |
353 | HashMap relationships = createRelationships(resource);
354 | if (relationships != null) {
355 | resourceRepresentation.put("relationships", relationships);
356 | }
357 |
358 | dataArray.add(resourceRepresentation);
359 | }
360 |
361 | return dataArray;
362 | }
363 |
364 | /**
365 | * Create data represenation from resource.
366 | * This will return the repersentation of the resource. The map contains the id, type and
367 | * relationships (if any). Attributes are only included when 'includeAttributes' is true.
368 | *
369 | * @param resource Resource to create data.
370 | * @param includeAttributes Add attributes map to representation.
371 | * @return Hashmaps.
372 | */
373 | public HashMap createData(Resource resource,
374 | boolean includeAttributes) {
375 | String resourceName = null;
376 | try {
377 | resourceName = nameForResourceClass(resource.getClass());
378 | } catch (Exception e) {
379 | Logger.debug(e.getMessage());
380 | return null;
381 | }
382 |
383 | HashMap resourceRepresentation = new HashMap<>();
384 | resourceRepresentation.put("type", resourceName);
385 | resourceRepresentation.put("id", resource.getId());
386 | if (includeAttributes) {
387 | HashMap attributes = serializer.getFieldsAsDictionary(resource);
388 | if (attributes != null) {
389 | resourceRepresentation.put("attributes", attributes);
390 | }
391 | }
392 |
393 | HashMap relationships = createRelationships(resource);
394 | if (relationships != null) {
395 | resourceRepresentation.put("relationships", relationships);
396 | }
397 |
398 | if (resource.getLinks() != null) {
399 | resourceRepresentation.put("links",
400 | createLinks(resource));
401 | }
402 |
403 | return resourceRepresentation;
404 | }
405 |
406 | /**
407 | * Creates the relationships represenation from an resource.
408 | * Will go through the relationships of a resource and return them as a map.
409 | * The keys of the returned map will be the resource type of the relationship and the value a map
410 | * of the data or a list of maps containing data of multiple relations.
411 | *
412 | * @param resource Resource to create relationships from.
413 | * @return HashMap of related resource names with their data.
414 | */
415 | public HashMap createRelationships(Resource resource) {
416 | HashMap relations = serializer.getRelationships(resource);
417 | HashMap relationships = new HashMap<>();
418 |
419 | for (String relationshipName : relations.keySet()) {
420 | Object relationObject = relations.get(relationshipName);
421 |
422 | if (relationObject instanceof Resource) {
423 | if (resource.getNullableRelationships().contains(relationshipName)) {
424 | HashMap dataObject = new HashMap<>();
425 | dataObject.put("data", null);
426 | relationships.put(relationshipName, dataObject);
427 | continue;
428 | }
429 |
430 | HashMap data = createData((Resource) relationObject, false);
431 | if (data != null) {
432 | HashMap dataObject = new HashMap<>();
433 | dataObject.put("data", data);
434 | relationships.put(relationshipName, dataObject);
435 | }
436 | }
437 |
438 | if (relationObject instanceof ArrayList) {
439 | if (resource.getNullableRelationships().contains(relationshipName)) {
440 | HashMap dataObject = new HashMap<>();
441 | dataObject.put("data", new ArrayList());
442 | relationships.put(relationshipName, dataObject);
443 | continue;
444 | }
445 |
446 | ArrayList dataArray = createData((List) relationObject, false);
447 | if (dataArray != null) {
448 | HashMap dataObject = new HashMap<>();
449 | dataObject.put("data", dataArray);
450 | relationships.put(relationshipName, dataObject);
451 | }
452 | }
453 | }
454 |
455 | if (relationships.isEmpty()) {
456 | relationships = null;
457 | }
458 |
459 | return relationships;
460 | }
461 |
462 | /**
463 | * Returns a resources links as a map.
464 | *
465 | * @param resource Resource to get links from.
466 | * @return Map of the links.
467 | */
468 | public HashMap createLinks(Resource resource) {
469 | HashMap links = null;
470 |
471 | Links resourceLinks = resource.getLinks();
472 | if (resourceLinks != null) {
473 | links = new HashMap<>();
474 | if (resourceLinks.getSelfLink() != null) {
475 | links.put("self", resourceLinks.getSelfLink());
476 | }
477 | if (resourceLinks.getRelated() != null) {
478 | links.put("related", resourceLinks.getRelated());
479 | }
480 | if (resourceLinks.getFirst() != null) {
481 | links.put("first", resourceLinks.getFirst());
482 | }
483 | if (resourceLinks.getLast() != null) {
484 | links.put("last", resourceLinks.getLast());
485 | }
486 | if (resourceLinks.getPrev() != null) {
487 | links.put("prev", resourceLinks.getPrev());
488 | }
489 | if (resourceLinks.getNext() != null) {
490 | links.put("next", resourceLinks.getNext());
491 | }
492 | if (resourceLinks.getAbout() != null) {
493 | links.put("about", resourceLinks.getAbout());
494 | }
495 | }
496 |
497 | return links;
498 | }
499 |
500 | /**
501 | * Create the included as list of maps.
502 | *
503 | * @param resource Resource with relations.
504 | * @return List of maps.
505 | */
506 | public ArrayList> createIncluded(Resource resource) {
507 | HashMap relations = serializer.getRelationships(resource);
508 | ArrayList> includes = new ArrayList<>();
509 |
510 | for (String relationshipName : relations.keySet()) {
511 | Object relationObject = relations.get(relationshipName);
512 | if (relationObject instanceof Resource) {
513 | HashMap data = createData((Resource) relationObject, true);
514 | if (data != null) {
515 | includes.add(data);
516 | }
517 | }
518 |
519 | if (relationObject instanceof ArrayList) {
520 | ArrayList dataArray = createData((List) relationObject, true);
521 | if (dataArray != null) {
522 | includes.addAll(dataArray);
523 | }
524 | }
525 | }
526 |
527 | return includes;
528 | }
529 |
530 |
531 | // helper
532 |
533 | /**
534 | * Get the annotated relationship names.
535 | *
536 | * @param clazz Class for annotation.
537 | * @return List of relationship names.
538 | */
539 | private HashMap getRelationshipNames(Class clazz) {
540 | HashMap relationNames = new HashMap<>();
541 | for (Field field : clazz.getDeclaredFields()) {
542 | String fieldName = field.getName();
543 | for (Annotation annotation : field.getDeclaredAnnotations()) {
544 | if (annotation.annotationType() == SerializedName.class) {
545 | SerializedName serializeName = (SerializedName)annotation;
546 | fieldName = serializeName.value();
547 | }
548 | if (annotation.annotationType() == Relationship.class) {
549 | Relationship relationshipAnnotation = (Relationship)annotation;
550 | relationNames.put(relationshipAnnotation.value(), fieldName);
551 | }
552 | }
553 | }
554 |
555 | return relationNames;
556 | }
557 |
558 | private String nameForResourceClass(Class clazz) throws Exception {
559 | for (String key : Deserializer.getRegisteredClasses().keySet()) {
560 | if (Deserializer.getRegisteredClasses().get(key) == clazz) {
561 | return key;
562 | }
563 | }
564 |
565 | throw new Exception("Class " + clazz.getSimpleName() + " not registered.");
566 | }
567 |
568 | // getter
569 |
570 | public Deserializer getDeserializer() {
571 | return deserializer;
572 | }
573 |
574 | public AttributeMapper getAttributeMapper() {
575 | return attributeMapper;
576 | }
577 |
578 | public Serializer getSerializer() {
579 | return serializer;
580 | }
581 | }
582 |
--------------------------------------------------------------------------------
/morpheus/src/test/java/at/rags/morpheus/MapperUnitTest.java:
--------------------------------------------------------------------------------
1 | package at.rags.morpheus;
2 |
3 | import org.json.JSONArray;
4 | import org.json.JSONException;
5 | import org.json.JSONObject;
6 | import org.junit.Before;
7 | import org.junit.Test;
8 | import org.mockito.ArgumentCaptor;
9 | import org.mockito.Matchers;
10 | import org.mockito.Mockito;
11 |
12 | import java.lang.reflect.Array;
13 | import java.lang.reflect.Field;
14 | import java.util.ArrayList;
15 | import java.util.HashMap;
16 | import java.util.List;
17 | import java.util.Objects;
18 |
19 | import at.rags.morpheus.Exceptions.NotExtendingResourceException;
20 | import at.rags.morpheus.TestResources.Article;
21 | import at.rags.morpheus.TestResources.Author;
22 |
23 | import static junit.framework.Assert.assertEquals;
24 | import static junit.framework.Assert.assertNotNull;
25 | import static junit.framework.Assert.assertNull;
26 | import static org.junit.Assert.*;
27 | import static org.mockito.Matchers.any;
28 | import static org.mockito.Matchers.anyObject;
29 | import static org.mockito.Matchers.anyString;
30 | import static org.mockito.Matchers.eq;
31 | import static org.mockito.Mockito.mock;
32 | import static org.mockito.Mockito.verify;
33 | import static org.mockito.Mockito.when;
34 |
35 | public class MapperUnitTest {
36 |
37 | private Mapper mapper;
38 | private Mapper newMapper;
39 | private Deserializer mockDeserializer;
40 | private Serializer mockSerializer;
41 | private AttributeMapper mockAttributeMapper;
42 |
43 | @Before
44 | public void setup() {
45 | mapper = new Mapper();
46 | Deserializer.setRegisteredClasses(new HashMap());
47 |
48 | mockDeserializer = Mockito.mock(Deserializer.class);
49 | mockSerializer = Mockito.mock(Serializer.class);
50 | mockAttributeMapper = Mockito.mock(AttributeMapper.class);
51 |
52 | newMapper = new Mapper(mockDeserializer, mockSerializer, mockAttributeMapper);
53 | }
54 |
55 | @Test
56 | public void testInit() throws Exception {
57 | Mapper mapper = new Mapper();
58 | assertNotNull(mapper);
59 | }
60 |
61 | @Test
62 | public void testMapLinks() throws Exception {
63 | JSONObject jsonObject = mock(JSONObject.class);
64 | when(jsonObject.getString("self")).thenReturn("www.self.com");
65 | when(jsonObject.getString("related")).thenReturn("www.related.com");
66 | when(jsonObject.getString("first")).thenReturn("www.first.com");
67 | when(jsonObject.getString("last")).thenReturn("www.last.com");
68 | when(jsonObject.getString("prev")).thenReturn("www.prev.com");
69 | when(jsonObject.getString("next")).thenReturn("www.next.com");
70 |
71 | Links links = mapper.mapLinks(jsonObject);
72 |
73 | assertTrue(links.getSelfLink().equals("www.self.com"));
74 | assertTrue(links.getRelated().equals("www.related.com"));
75 | assertTrue(links.getFirst().equals("www.first.com"));
76 | assertTrue(links.getLast().equals("www.last.com"));
77 | assertTrue(links.getPrev().equals("www.prev.com"));
78 | assertTrue(links.getNext().equals("www.next.com"));
79 | }
80 |
81 | @Test
82 | public void testMapLinksJSONException() throws Exception {
83 | JSONObject jsonObject = mock(JSONObject.class);
84 | when(jsonObject.getString("self")).thenThrow(new JSONException(""));
85 | when(jsonObject.getString("related")).thenThrow(new JSONException(""));
86 | when(jsonObject.getString("first")).thenThrow(new JSONException(""));
87 | when(jsonObject.getString("last")).thenThrow(new JSONException(""));
88 | when(jsonObject.getString("prev")).thenThrow(new JSONException(""));
89 | when(jsonObject.getString("next")).thenThrow(new JSONException(""));
90 |
91 | Links links = mapper.mapLinks(jsonObject);
92 |
93 | assertNull(links.getSelfLink());
94 | assertNull(links.getRelated());
95 | assertNull(links.getFirst());
96 | assertNull(links.getLast());
97 | assertNull(links.getPrev());
98 | assertNull(links.getNext());
99 | }
100 |
101 | @Test
102 | public void testMapId() throws Exception {
103 | Deserializer mockDeserializer = mock(Deserializer.class);
104 | Mapper mapper = new Mapper(mockDeserializer, null, null);
105 | JSONObject jsonObject = mock(JSONObject.class);
106 | Resource resource = new Resource();
107 | resource.setId("123456");
108 |
109 | when(mockDeserializer.
110 | setIdField(Matchers.anyObject(), anyObject()))
111 | .thenReturn(resource);
112 |
113 | resource = mapper.mapId(resource, jsonObject);
114 |
115 | assertTrue(resource.getId().equals("123456"));
116 | }
117 |
118 |
119 | @Test(expected = NotExtendingResourceException.class)
120 | public void testMapIdNotExtendingException() throws Exception {
121 | Deserializer mockDeserializer = mock(Deserializer.class);
122 | Mapper mapper = new Mapper(mockDeserializer, null, null);
123 | JSONObject jsonObject = mock(JSONObject.class);
124 | Resource resource = new Resource();
125 |
126 | when(mockDeserializer.
127 | setIdField(Matchers.anyObject(), anyObject()))
128 | .thenThrow(new NotExtendingResourceException(""));
129 |
130 | resource = mapper.mapId(resource, jsonObject);
131 | }
132 |
133 | @Test
134 | public void testMapIdJSONException() throws Exception {
135 | JSONObject jsonObject = mock(JSONObject.class);
136 | Resource resource = new Resource();
137 |
138 | when(jsonObject.get(anyString())).thenThrow(new JSONException(""));
139 |
140 | resource = mapper.mapId(resource, jsonObject);
141 |
142 | assertNull(resource.getId());
143 | }
144 |
145 | @Test
146 | public void testMapAttributesMapping() throws Exception {
147 | JSONObject jsonObject = mock(JSONObject.class);
148 | JSONArray jsonArray = mock(JSONArray.class);
149 | AttributeMapper mockAttributeMapper = mock(AttributeMapper.class);
150 | Mapper mapper = new Mapper(new Deserializer(), new Serializer(), mockAttributeMapper);
151 |
152 | Article article = new Article();
153 | article.setId("1");
154 |
155 | mapper.mapAttributes(article, jsonObject);
156 |
157 | ArgumentCaptor fieldArgumentCaptor = ArgumentCaptor.forClass(Field.class);
158 |
159 | verify(mockAttributeMapper).mapAttributeToObject(Matchers.anyObject(),
160 | any(JSONObject.class), any(Field.class), eq("title"));
161 | verify(mockAttributeMapper).mapAttributeToObject(Matchers.anyObject(),
162 | any(JSONObject.class), fieldArgumentCaptor.capture(), eq("public"));
163 | verify(mockAttributeMapper).mapAttributeToObject(Matchers.anyObject(),
164 | any(JSONObject.class), any(Field.class), eq("tags"));
165 | verify(mockAttributeMapper).mapAttributeToObject(Matchers.anyObject(),
166 | any(JSONObject.class), any(Field.class), eq("map"));
167 | verify(mockAttributeMapper).mapAttributeToObject(Matchers.anyObject(),
168 | any(JSONObject.class), any(Field.class), eq("version"));
169 | verify(mockAttributeMapper).mapAttributeToObject(Matchers.anyObject(),
170 | any(JSONObject.class), any(Field.class), eq("price"));
171 |
172 | assertEquals(fieldArgumentCaptor.getValue().getName(), "publicStatus");
173 | }
174 |
175 | @Test
176 | public void testMapRelationsExceptions() throws Exception {
177 | Deserializer mockDeserializer = mock(Deserializer.class);
178 | Mapper mapper = new Mapper(mockDeserializer, null, null);
179 | JSONObject jsonObject = mock(JSONObject.class);
180 | List mockIncluded = mock(List.class);
181 |
182 | when(jsonObject.getJSONObject(anyString())).thenThrow(new JSONException(""));
183 |
184 | Article article = new Article();
185 | mapper.mapRelations(article, jsonObject, mockIncluded);
186 |
187 | assertNull(article.getAuthor());
188 | }
189 |
190 | @Test
191 | public void testMapRelationsObjectRelation() throws Exception {
192 | Deserializer mockDeserializer = mock(Deserializer.class);
193 | Mapper mapper = new Mapper(mockDeserializer, null, null);
194 | Factory.setDeserializer(mockDeserializer);
195 | Factory.setMapper(mapper);
196 | JSONObject jsonObject = mock(JSONObject.class);
197 | JSONObject relationObject = mock(JSONObject.class);
198 | JSONObject authorObject = mock(JSONObject.class);
199 | List included = new ArrayList<>();
200 |
201 | Author includedAuthor = new Author();
202 | includedAuthor.setId("1");
203 | includedAuthor.setName("James");
204 | included.add(includedAuthor);
205 |
206 | Author author = new Author();
207 | author.setId("1");
208 |
209 | when(mockDeserializer.createObjectFromString(anyString())).thenReturn(author);
210 | when(jsonObject.getJSONObject(eq("author"))).thenReturn(relationObject);
211 | when(jsonObject.getJSONObject(eq("authors"))).thenThrow(new JSONException(""));
212 | when(relationObject.getJSONObject(anyString())).thenReturn(authorObject);
213 | when(relationObject.getJSONArray(anyString())).thenThrow(new JSONException(""));
214 | when(authorObject.getString("type")).thenReturn("author");
215 | when(authorObject.getJSONObject("links")).thenReturn(new JSONObject());
216 | when(mockDeserializer.setIdField(Matchers.anyObject(),
217 | any(JSONObject.class))).thenReturn(author);
218 |
219 | Article article = new Article();
220 | Article mappedArticle = (Article)mapper.mapRelations(article, jsonObject, included);
221 |
222 | ArgumentCaptor objectArgumentCaptor = ArgumentCaptor.forClass(Object.class);
223 |
224 | verify(mockDeserializer).setField(Matchers.anyObject(), eq("author"), objectArgumentCaptor.capture());
225 |
226 | Author resultAuthor = (Author)objectArgumentCaptor.getValue();
227 | assertEquals(resultAuthor.getId(), "1");
228 | assertEquals(resultAuthor.getName(), "James");
229 | }
230 |
231 | @Test
232 | public void testMapRelationsObjectRelationWithoutInclude() throws Exception {
233 | Deserializer mockDeserializer = mock(Deserializer.class);
234 | Mapper mapper = new Mapper(mockDeserializer, null, null);
235 | Factory.setDeserializer(mockDeserializer);
236 | Factory.setMapper(mapper);
237 | JSONObject jsonObject = mock(JSONObject.class);
238 | JSONObject relationObject = mock(JSONObject.class);
239 | JSONObject authorObject = mock(JSONObject.class);
240 |
241 | Author author = new Author();
242 | author.setId("1");
243 | author.setName("Name");
244 |
245 | when(mockDeserializer.createObjectFromString(anyString())).thenReturn(author);
246 | when(jsonObject.getJSONObject(eq("author"))).thenReturn(relationObject);
247 | when(jsonObject.getJSONObject(eq("authors"))).thenThrow(new JSONException(""));
248 | when(relationObject.getJSONObject(anyString())).thenReturn(authorObject);
249 | when(relationObject.getJSONArray(anyString())).thenThrow(new JSONException(""));
250 | when(authorObject.getString("type")).thenReturn("author");
251 | when(authorObject.getJSONObject("links")).thenReturn(new JSONObject());
252 | when(mockDeserializer.setIdField(Matchers.anyObject(),
253 | any(JSONObject.class))).thenReturn(author);
254 |
255 | Article article = new Article();
256 | Article mappedArticle = (Article)mapper.mapRelations(article, jsonObject, null);
257 |
258 | ArgumentCaptor objectArgumentCaptor = ArgumentCaptor.forClass(Object.class);
259 |
260 | verify(mockDeserializer).setField(Matchers.anyObject(), eq("author"), objectArgumentCaptor.capture());
261 |
262 | Author resultAuthor = (Author)objectArgumentCaptor.getValue();
263 | assertEquals(resultAuthor.getId(), "1");
264 | assertEquals(resultAuthor.getName(), "Name");
265 | assertNotNull(mappedArticle);
266 | }
267 |
268 | @Test
269 | public void testMapRelationsObjectsRelation() throws Exception {
270 | Deserializer mockDeserializer = mock(Deserializer.class);
271 | Mapper mapper = new Mapper(mockDeserializer,null, null);
272 | Factory.setDeserializer(mockDeserializer);
273 | Factory.setMapper(mapper);
274 | JSONObject jsonObject = mock(JSONObject.class);
275 | JSONObject relationObject = mock(JSONObject.class);
276 | JSONArray authorObjects = mock(JSONArray.class);
277 | JSONObject authorObject = mock(JSONObject.class);
278 | List included = new ArrayList<>();
279 |
280 | Author includedAuthor = new Author();
281 | includedAuthor.setId("1");
282 | includedAuthor.setName("James");
283 | included.add(includedAuthor);
284 |
285 | Author author1 = new Author();
286 | author1.setId("1");
287 |
288 | Author author2 = new Author();
289 | author2.setId("2");
290 |
291 | when(mockDeserializer.createObjectFromString(anyString())).thenReturn(author1);
292 | when(jsonObject.getJSONObject(anyString())).thenReturn(relationObject);
293 | when(relationObject.getJSONArray(anyString())).thenReturn(authorObjects);
294 | when(relationObject.getJSONObject(anyString())).thenThrow(new JSONException(""));
295 | when(authorObjects.length()).thenReturn(2);
296 | when(authorObjects.getJSONObject(0)).thenReturn(authorObject);
297 | when(authorObjects.getJSONObject(1)).thenReturn(authorObject);
298 | when(authorObject.getJSONObject("links")).thenReturn(new JSONObject());
299 | when(mockDeserializer.setIdField(Matchers.anyObject(),
300 | any(JSONObject.class))).thenReturn(author1);
301 |
302 | Article article = new Article();
303 | Article mappedArticle = (Article)mapper.mapRelations(article, jsonObject, included);
304 |
305 | ArgumentCaptor objectArgumentCaptor = ArgumentCaptor.forClass(List.class);
306 |
307 | verify(mockDeserializer).setField(Matchers.anyObject(), eq("authors"), objectArgumentCaptor.capture());
308 |
309 | Author resultAuthor = (Author)objectArgumentCaptor.getValue().get(0);
310 | assertEquals(resultAuthor.getId(), "1");
311 | assertEquals(resultAuthor.getName(), "James");
312 | }
313 |
314 | @Test
315 | public void testCreateDataFromJsonResources() {
316 | Deserializer.registerResourceClass("authors", Author.class);
317 |
318 | Author author = new Author();
319 | author.setId("id");
320 | author.setName("name");
321 | ArrayList authors = new ArrayList<>();
322 | authors.add(author);
323 | authors.add(author);
324 |
325 | HashMap authorMap = new HashMap<>();
326 | authorMap.put("id","id");
327 | authorMap.put("type", "authors");
328 | authorMap.put("attributes", mapper.getSerializer().getFieldsAsDictionary(author));
329 |
330 | ArrayList> dataArray = new ArrayList<>();
331 | dataArray.add(authorMap);
332 | dataArray.add(authorMap);
333 |
334 | ArrayList> output = mapper
335 | .createData((List)authors, true);
336 |
337 | assertNotNull(output);
338 | assertEquals(output.toString(), dataArray.toString());
339 | }
340 |
341 | @Test
342 | public void testCreateDataFromJsonResource() {
343 | Deserializer.registerResourceClass("authors", Author.class);
344 |
345 | Author author = new Author();
346 | author.setId("id");
347 | author.setName("name");
348 |
349 | HashMap authorMap = new HashMap<>();
350 | authorMap.put("id","id");
351 | authorMap.put("type", "authors");
352 | authorMap.put("attributes", mapper.getSerializer().getFieldsAsDictionary(author));
353 |
354 | HashMap output = mapper
355 | .createData(author, true);
356 |
357 | assertNotNull(output);
358 | assertEquals(3, output.size());
359 | assertEquals(output.toString(), authorMap.toString());
360 | }
361 |
362 | @Test
363 | public void testCreateDataFromJsonResourceWithRelationship() {
364 | Deserializer.registerResourceClass("authors", Author.class);
365 | Deserializer.registerResourceClass("articles", Article.class);
366 |
367 | Author author = new Author();
368 | author.setId("id");
369 | author.setName("name");
370 |
371 | Article article = new Article();
372 | article.setId("articleId");
373 | article.setTitle("Some title");
374 | article.setPrice(1.0);
375 | article.setPublicStatus(true);
376 | article.setMap(null);
377 | article.setVersion(10);
378 | article.setTags(null);
379 | article.setAuthor(author);
380 |
381 | HashMap relationDataMap = new HashMap<>();
382 | relationDataMap.put("data", mapper.createData(author, false));
383 |
384 | HashMap relationMap = new HashMap<>();
385 | relationMap.put("author", relationDataMap);
386 |
387 | HashMap articleMap = new HashMap<>();
388 | articleMap.put("id","articleId");
389 | articleMap.put("type", "articles");
390 | articleMap.put("attributes", mapper.getSerializer().getFieldsAsDictionary(article));
391 | articleMap.put("relationships", relationMap);
392 |
393 |
394 | HashMap output = mapper
395 | .createData(article, true);
396 |
397 | assertNotNull(output);
398 | assertEquals(output.toString(), articleMap.toString());
399 | }
400 |
401 | @Test
402 | public void testCreateDataFromJsonResourceWithLinks() {
403 | Deserializer.registerResourceClass("authors", Author.class);
404 |
405 | Author author = new Author();
406 | author.setId("id");
407 |
408 | Links links = new Links();
409 | links.setSelfLink("self.com");
410 | links.setRelated("related.com");
411 | author.setLinks(links);
412 |
413 | HashMap linkMap = new HashMap<>();
414 | linkMap.put("self", "self.com");
415 | linkMap.put("related", "related.com");
416 |
417 | HashMap authorMap = new HashMap<>();
418 | authorMap.put("id","id");
419 | authorMap.put("type", "authors");
420 | authorMap.put("links", linkMap);
421 |
422 | HashMap output = mapper
423 | .createData(author, true);
424 |
425 | assertNotNull(output);
426 | assertEquals(authorMap.toString(), output.toString());
427 | }
428 |
429 | @Test
430 | public void testCreateRelationshipsFromResource() {
431 | Deserializer.registerResourceClass("authors", Author.class);
432 | Deserializer.registerResourceClass("articles", Article.class);
433 |
434 | Author author = new Author();
435 | author.setId("authorId");
436 |
437 | Article article = new Article();
438 | article.setId("articleId");
439 | article.setTitle("Some title");
440 | article.setAuthor(author);
441 |
442 | HashMap authorMap = new HashMap<>();
443 |
444 | HashMap relationDataMap = new HashMap<>();
445 | relationDataMap.put("data", mapper.createData(author, false));
446 |
447 | authorMap.put("author", relationDataMap);
448 |
449 | HashMap output = mapper.createRelationships(article);
450 |
451 | assertNotNull(output);
452 | assertEquals(authorMap.toString(), output.toString());
453 | }
454 |
455 | @Test
456 | public void testCreateRelationshipsFromResourceReturnsNullWithNoRelations() {
457 | Deserializer.registerResourceClass("authors", Author.class);
458 | Deserializer.registerResourceClass("articles", Article.class);
459 |
460 | Article article = new Article();
461 | article.setId("articleId");
462 | article.setTitle("Some title");
463 |
464 | HashMap output = mapper.createRelationships(article);
465 |
466 | assertEquals(null, output);
467 | }
468 |
469 | @Test
470 | public void testCreateRelationshipsFromResources() {
471 | Deserializer.registerResourceClass("authors", Author.class);
472 | Deserializer.registerResourceClass("articles", Article.class);
473 |
474 | Author author = new Author();
475 | author.setId("authorId");
476 | author.setName("Hans");
477 |
478 | ArrayList authors = new ArrayList<>();
479 | authors.add(author);
480 | authors.add(author);
481 |
482 | Article article = new Article();
483 | article.setId("articleId");
484 | article.setTitle("Some title");
485 | article.setAuthor(author);
486 | article.setAuthors(authors);
487 |
488 | ArrayList articles = new ArrayList<>();
489 | articles.add(article);
490 | articles.add(article);
491 |
492 | HashMap relationships = new HashMap<>();
493 | relationships.put("author", author);
494 |
495 | Mockito.stub(mockSerializer.getFieldsAsDictionary(eq(author))).toReturn(null);
496 | Mockito.stub(mockSerializer.getRelationships(eq(article))).toReturn(relationships);
497 |
498 |
499 | ArrayList> data = newMapper.createData(articles, false);
500 |
501 |
502 | HashMap relationshipsMap =
503 | (HashMap) data.get(0).get("relationships");
504 | HashMap authorMap = (HashMap) relationshipsMap.get("author");
505 | HashMap authorData =
506 | (HashMap) authorMap.get("data");
507 |
508 | assertNotNull(data);
509 | assertNotNull(data.get(0).get("relationships"));
510 | assertNotNull(relationshipsMap.get("author"));
511 | assertNotNull(authorMap);
512 | assertNotNull(authorData);
513 | assertEquals(2, authorData.size());
514 | assertEquals("authors", authorData.get("type"));
515 | assertEquals("authorId", authorData.get("id"));
516 | }
517 |
518 | @Test
519 | public void testCreateRelationshipsNulling() {
520 | Deserializer.registerResourceClass("authors", Author.class);
521 | Deserializer.registerResourceClass("articles", Article.class);
522 |
523 | Author author = new Author();
524 | author.setId("authorId");
525 | author.setName("Hans");
526 |
527 | ArrayList authors = new ArrayList<>();
528 | authors.add(author);
529 | authors.add(author);
530 |
531 | Article article = new Article();
532 | article.setId("articleId");
533 | article.setTitle("Some title");
534 | article.setAuthor(author);
535 | article.setAuthors(authors);
536 |
537 | ArrayList articles = new ArrayList<>();
538 | articles.add(article);
539 | articles.add(article);
540 |
541 | HashMap relationships = new HashMap<>();
542 | relationships.put("author", author);
543 | relationships.put("authors", authors);
544 |
545 | article.addRelationshipToNull("author");
546 | article.addRelationshipToNull("authors");
547 |
548 | Mockito.stub(mockSerializer.getFieldsAsDictionary(eq(author))).toReturn(null);
549 | Mockito.stub(mockSerializer.getRelationships(eq(article))).toReturn(relationships);
550 |
551 |
552 | ArrayList> data = newMapper.createData(articles, false);
553 |
554 |
555 | HashMap relationshipsMap =
556 | (HashMap) data.get(0).get("relationships");
557 |
558 |
559 | assertNotNull(data);
560 | assertNotNull(data.get(0).get("relationships"));
561 | HashMap authorData = (HashMap) relationshipsMap.get("author");
562 | assertNull(authorData.get("data"));
563 |
564 | HashMap relationAuthors = (HashMap) relationshipsMap.get("authors");
565 | ArrayList relationData = (ArrayList) relationAuthors.get("data");
566 | assertEquals(0, relationData.size());
567 | }
568 |
569 | @Test
570 | public void testCreateLinksFromResource() {
571 | HashMap checkLinks = new HashMap<>();
572 | checkLinks.put("self", "selflink.com");
573 | checkLinks.put("related", "related.com");
574 | checkLinks.put("first", "first.com");
575 | checkLinks.put("last", "last.com");
576 | checkLinks.put("prev", "prev.com");
577 | checkLinks.put("next", "next.com");
578 | checkLinks.put("about", "about.com");
579 |
580 | Resource resource = new Resource();
581 | Links links = new Links();
582 | links.setSelfLink("selflink.com");
583 | links.setRelated("related.com");
584 | links.setFirst("first.com");
585 | links.setLast("last.com");
586 | links.setPrev("prev.com");
587 | links.setNext("next.com");
588 | links.setAbout("about.com");
589 | resource.setLinks(links);
590 |
591 | HashMap linksFromResource =
592 | mapper.createLinks(resource);
593 |
594 | assertEquals(linksFromResource, checkLinks);
595 | }
596 |
597 | @Test
598 | public void testCreateIncluded() {
599 | Deserializer.registerResourceClass("authors", Author.class);
600 | Deserializer.registerResourceClass("articles", Article.class);
601 |
602 | Author author = new Author();
603 | author.setId("authorId");
604 | author.setName("Hans");
605 |
606 | ArrayList authors = new ArrayList<>();
607 | authors.add(author);
608 | authors.add(author);
609 |
610 | Article article = new Article();
611 | article.setId("articleId");
612 | article.setTitle("Some title");
613 | article.setAuthor(author);
614 | article.setAuthors(authors);
615 |
616 | HashMap relations = new HashMap<>();
617 | relations.put("author", author);
618 | relations.put("authors", authors);
619 |
620 | ArrayList> checkIncluded = new ArrayList<>();
621 | HashMap authorData = new HashMap<>();
622 | authorData.put("name", "Hans");
623 | checkIncluded.add(authorData);
624 |
625 | Mockito.stub(mockSerializer.getRelationships(eq(article))).toReturn(relations);
626 | Mockito.stub(mockSerializer.getFieldsAsDictionary(eq(author))).toReturn(authorData);
627 |
628 | ArrayList> included = newMapper.createIncluded(article);
629 |
630 | assertNotNull(included);
631 | assertEquals(3, included.size());
632 | assertEquals("authors", included.get(0).get("type"));
633 | assertEquals("authorId", included.get(0).get("id"));
634 | assertEquals(authorData, included.get(0).get("attributes"));
635 | }
636 | }
637 |
--------------------------------------------------------------------------------