All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.jasminb.jsonapi.ConverterConfiguration Maven / Gradle / Ivy

Go to download

JSONAPI-Converter is a library that provides means for integrating with services using JSON API specification.

There is a newer version: 0.14
Show newest version
package com.github.jasminb.jsonapi;

import com.github.jasminb.jsonapi.annotations.Id;
import com.github.jasminb.jsonapi.annotations.Meta;
import com.github.jasminb.jsonapi.annotations.Relationship;
import com.github.jasminb.jsonapi.annotations.RelationshipLinks;
import com.github.jasminb.jsonapi.annotations.RelationshipMeta;
import com.github.jasminb.jsonapi.annotations.Type;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Converter configuration.
 *
 * 

* This class exposes contracts needed to handle JSON API spec serialization/deserialization. *

* * @author jbegic */ public class ConverterConfiguration { private final Map> typeToClassMapping = new HashMap<>(); private final Map, Type> typeAnnotations = new HashMap<>(); private final Map, Field> idMap = new HashMap<>(); private final Map, ResourceIdHandler> idHandlerMap = new HashMap<>(); private final Map, List> relationshipMap = new HashMap<>(); private final Map, Map>> relationshipTypeMap = new HashMap<>(); private final Map, Map> relationshipFieldMap = new HashMap<>(); private final Map fieldRelationshipMap = new HashMap<>(); private final Map fieldRelationshipMetaMap = new HashMap<>(); private final Map, Map>> relationshipMetaTypeMap = new HashMap<>(); private final Map, Map> relationshipMetaFieldMap = new HashMap<>(); private final Map, Class> metaTypeMap = new HashMap<>(); private final Map, Field> metaFieldMap = new HashMap<>(); private final Map, Field> linkFieldMap = new HashMap<>(); // Relationship links lookups private final Map, Map> relationshipLinksFieldMap = new HashMap<>(); /** * Creates new ConverterConfiguration. * @param classes list of mapped classes */ public ConverterConfiguration(Class... classes) { for (Class clazz : classes) { registerType(clazz); } } private void processClass(Class clazz) { if (clazz.isAnnotationPresent(Type.class)) { Type annotation = clazz.getAnnotation(Type.class); typeToClassMapping.put(annotation.value(), clazz); typeAnnotations.put(clazz, annotation); relationshipTypeMap.put(clazz, new HashMap>()); relationshipFieldMap.put(clazz, new HashMap()); relationshipMetaFieldMap.put(clazz, new HashMap()); relationshipMetaTypeMap.put(clazz, new HashMap>()); relationshipLinksFieldMap.put(clazz, new HashMap()); // collecting Relationship fields List relationshipFields = ReflectionUtils.getAnnotatedFields(clazz, Relationship.class, true); for (Field relationshipField : relationshipFields) { relationshipField.setAccessible(true); Relationship relationship = relationshipField.getAnnotation(Relationship.class); Class targetType = ReflectionUtils.getFieldType(relationshipField); relationshipTypeMap.get(clazz).put(relationship.value(), targetType); relationshipFieldMap.get(clazz).put(relationship.value(), relationshipField); fieldRelationshipMap.put(relationshipField, relationship); if (relationship.resolve() && relationship.relType() == null) { throw new IllegalArgumentException("@Relationship on " + clazz.getName() + "#" + relationshipField.getName() + " with 'resolve = true' must have a relType attribute " + "set." ); } registerType(targetType); } relationshipMap.put(clazz, relationshipFields); // collecting RelationshipMeta fields List relMetaFields = ReflectionUtils.getAnnotatedFields(clazz, RelationshipMeta.class, true); for (Field relMetaField : relMetaFields) { relMetaField.setAccessible(true); RelationshipMeta relationshipMeta = relMetaField.getAnnotation(RelationshipMeta.class); Class targetType = ReflectionUtils.getFieldType(relMetaField); relationshipMetaTypeMap.get(clazz).put(relationshipMeta.value(), targetType); fieldRelationshipMetaMap.put(relMetaField, relationshipMeta); relationshipMetaFieldMap.get(clazz).put(relationshipMeta.value(), relMetaField); } // Collecting RelationshipLink fields List relLinkFields = ReflectionUtils.getAnnotatedFields(clazz, RelationshipLinks.class, true); for (Field relLinkField : relLinkFields) { relLinkField.setAccessible(true); RelationshipLinks links = relLinkField.getAnnotation(RelationshipLinks.class); relationshipLinksFieldMap.get(clazz).put(links.value(), relLinkField); } // collecting Id fields List idAnnotatedFields = ReflectionUtils.getAnnotatedFields(clazz, Id.class, true); if (!idAnnotatedFields.isEmpty() && idAnnotatedFields.size() == 1) { Field idField = idAnnotatedFields.get(0); idField.setAccessible(true); idMap.put(clazz, idField); try { idHandlerMap.put(clazz, idField.getAnnotation(Id.class).value().newInstance()); } catch (Exception e) { throw new IllegalArgumentException("Unable to construct handler instance by using no-arg constructor", e); } } else { if (idAnnotatedFields.isEmpty()) { throw new IllegalArgumentException("All resource classes must have a field annotated with the " + "@Id annotation"); } else { throw new IllegalArgumentException("Only single @Id annotation is allowed per defined type!"); } } // Collecting Meta fields List metaFields = ReflectionUtils.getAnnotatedFields(clazz, Meta.class, true); if (metaFields.size() == 1) { Field metaField = metaFields.get(0); metaField.setAccessible(true); Class metaType = ReflectionUtils.getFieldType(metaField); metaTypeMap.put(clazz, metaType); metaFieldMap.put(clazz, metaField); } else if (metaFields.size() > 1) { throw new IllegalArgumentException(String.format("Only one meta field is allowed for type '%s'", clazz.getCanonicalName())); } // Collect and handle 'Link' field List linkFields = ReflectionUtils.getAnnotatedFields(clazz, com.github.jasminb.jsonapi.annotations.Links.class, true); if (linkFields.size() == 1) { Field linkField = linkFields.get(0); linkField.setAccessible(true); Class metaType = ReflectionUtils.getFieldType(linkField); if (!Links.class.isAssignableFrom(metaType)) { throw new IllegalArgumentException(String.format("%s is not allowed to be used as @Links " + "attribute. Only com.github.jasminb.jsonapi.Links or its derivatives" + " can be annotated as @Links", metaType.getCanonicalName())); } else { linkFieldMap.put(clazz, linkField); } } else if (linkFields.size() > 1) { throw new IllegalArgumentException(String.format("Only one links field is allowed for type '%s'", clazz.getCanonicalName())); } } else { throw new IllegalArgumentException("Class " + clazz.getName() + " don't have Type annotation. All resource classes must be annotated with Type annotation!"); } } /** * Returns field annotated with meta annotation for given type. * @param clazz {@link Class} type * @return {@link Field} meta field or null */ public Field getMetaField(Class clazz) { return metaFieldMap.get(clazz); } /** * Returns meta-data type for given class. * @param clazz {@link Class} class * @return {@link Class} type or null if no field with meta annotaiton is found on given type */ public Class getMetaType(Class clazz) { return metaTypeMap.get(clazz); } /** * Resolves link field for given type. * @param clazz {@link Class} type * @return {@link Field} or null */ public Field getLinksField(Class clazz) { return linkFieldMap.get(clazz); } /** * Resolves a type for given type name. * @param typeName {@link String} type name * @return {@link Class} resolved type */ public Class getTypeClass(String typeName) { return typeToClassMapping.get(typeName); } /** * Returns the id field for given type. * @param clazz {@link Class} type to resolve id field for * @return {@link Field} id field */ public Field getIdField(Class clazz) { return idMap.get(clazz); } /** * Returns handler registered for given type's id field. * * @param clazz {@link Class} type to resolve id handler for * @return handler */ public ResourceIdHandler getIdHandler(Class clazz) { return idHandlerMap.get(clazz); } /** * Returns relationship field. * @param clazz {@link Class} class holding relationship * @param fieldName {@link String} name of the field * @return {@link Field} field */ public Field getRelationshipField(Class clazz, String fieldName) { return relationshipFieldMap.get(clazz).get(fieldName); } /** * Returns a type of a relationship. * @param clazz {@link Class} owning the field with relationship annotation * @param fieldName {@link String} name of the field * @return {@link Class} field type */ public Class getRelationshipType(Class clazz, String fieldName) { return relationshipTypeMap.get(clazz).get(fieldName); } /** * Resolves {@link Relationship} instance for given field. * *

* This works for fields that were found to be annotated with {@link Relationship} annotation. *

* @param field {@link Field} to get relationship for * @return {@link Relationship} anotation or null */ public Relationship getFieldRelationship(Field field) { return fieldRelationshipMap.get(field); } /** * Returns list of all fields annotated with {@link Relationship} annotation for given class. * @param clazz {@link Class} to get relationship fields for * @return list of relationship fields */ public List getRelationshipFields(Class clazz) { return relationshipMap.get(clazz); } /** * Checks if provided class was registered with this configuration instance. * @param clazz {@link Class} to check * @return true if class was registed else false */ public boolean isRegisteredType(Class clazz) { return typeAnnotations.containsKey(clazz); } /** * Resolves and returns name of the type given to provided class. * @param clazz {@link Class} to resolve type name for * @return type name or null if type was not registered */ public String getTypeName(Class clazz) { Type type = typeAnnotations.get(clazz); if (type != null) { return type.value(); } return null; } /** * Resolves and returns the type given to provided class. * @param clazz {@link Class} to resolve type name for * @return type or null if type was not registered */ public Type getType(Class clazz) { return typeAnnotations.get(clazz); } /** * Registers new type with this configuration instance. * @param type {@link Class} type to register * @return true in case type was registered, false in case type was registered already */ public synchronized boolean registerType(Class type) { if (!isRegisteredType(type)) { processClass(type); return true; } return false; } /** * Checks if class is eligible as resource type (must have {@link Type} annotation and field annotated with * {@link Id}. * @param type type to test * @return true if type is eligible */ public static boolean isEligibleType(Class type) { return type.isAnnotationPresent(Type.class) && !ReflectionUtils.getAnnotatedFields(type, Id.class, true).isEmpty(); } /** * Returns relationship meta field. * @param clazz {@link Class} class holding relationship * @param relationshipName {@link String} name of the relationship * @return {@link Field} field */ public Field getRelationshipMetaField(Class clazz, String relationshipName) { return relationshipMetaFieldMap.get(clazz).get(relationshipName); } /** * Returns a type of a relationship meta field. * @param clazz {@link Class} owning the field with relationship meta annotation * @param relationshipName {@link String} name of the field * @return {@link Class} meta field type */ public Class getRelationshipMetaType(Class clazz, String relationshipName) { return relationshipMetaTypeMap.get(clazz).get(relationshipName); } /** * Returns relationship links field. * @param clazz {@link Class} class holding relationship * @param relationshipName {@link String} name of the relationship * @return {@link Field} field */ public Field getRelationshipLinksField(Class clazz, String relationshipName) { return relationshipLinksFieldMap.get(clazz).get(relationshipName); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy