From 0897bf395ffd4d5d9b8e71eb4fdaceed3bbec47d Mon Sep 17 00:00:00 2001 From: Asad Ali Date: Tue, 23 May 2023 17:51:12 +0500 Subject: [PATCH 1/4] added separate exceptions for Oneof and Anyof validations along with unit tests --- .../core/annotations/TypeCombinator.java | 2 + .../core/types/AnyOfValidationException.java | 25 + .../core/types/OneOfValidationException.java | 35 + .../apimatic/core/utilities/CoreHelper.java | 2661 +++++++++-------- .../java/apimatic/core/models/AtomCase.java | 2 +- .../java/apimatic/core/models/OrbitCase.java | 2 +- .../core/utilities/CoreHelperTest.java | 97 +- 7 files changed, 1468 insertions(+), 1356 deletions(-) create mode 100644 src/main/java/io/apimatic/core/types/AnyOfValidationException.java create mode 100644 src/main/java/io/apimatic/core/types/OneOfValidationException.java diff --git a/src/main/java/io/apimatic/core/annotations/TypeCombinator.java b/src/main/java/io/apimatic/core/annotations/TypeCombinator.java index 2aa77439..08180c91 100644 --- a/src/main/java/io/apimatic/core/annotations/TypeCombinator.java +++ b/src/main/java/io/apimatic/core/annotations/TypeCombinator.java @@ -4,6 +4,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; + import com.fasterxml.jackson.databind.JsonSerializer; /** @@ -13,6 +14,7 @@ public interface TypeCombinator { @Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @interface TypeCombinatorCase { + public String type() default ""; } @Retention(RetentionPolicy.RUNTIME) diff --git a/src/main/java/io/apimatic/core/types/AnyOfValidationException.java b/src/main/java/io/apimatic/core/types/AnyOfValidationException.java new file mode 100644 index 00000000..541a5e94 --- /dev/null +++ b/src/main/java/io/apimatic/core/types/AnyOfValidationException.java @@ -0,0 +1,25 @@ +package io.apimatic.core.types; + +import java.io.IOException; +import java.util.List; +import com.fasterxml.jackson.databind.JsonNode; + +/** + * This is the base class for all exceptions that represent an error response from the server. + */ +public class AnyOfValidationException extends IOException { + + /** + * UID for serialization. + */ + private static final long serialVersionUID = 1214174253911720228L; + + /** + * Initialization constructor. + * @param types List on unMapped types + * @param json Value that was not mapped by the above types + */ + public AnyOfValidationException(List types, JsonNode json) { + super("We could not match any acceptable type from " + String.join(", ", types) + " on: " + json ); + } +} diff --git a/src/main/java/io/apimatic/core/types/OneOfValidationException.java b/src/main/java/io/apimatic/core/types/OneOfValidationException.java new file mode 100644 index 00000000..3f6c13a9 --- /dev/null +++ b/src/main/java/io/apimatic/core/types/OneOfValidationException.java @@ -0,0 +1,35 @@ +package io.apimatic.core.types; + +import java.io.IOException; +import java.util.List; +import com.fasterxml.jackson.databind.JsonNode; + +/** + * This is the base class for all exceptions that represent an error response from the server. + */ +public class OneOfValidationException extends IOException { + + /** + * UID for serialization. + */ + private static final long serialVersionUID = 6424174253911720228L; + + /** + * Initialization constructor. + * @param type1 The first type that was mapped on jsonNode + * @param type2 The second type that also got mapped on jsonNode + * @param json Value that was mapped by the above two types + */ + public OneOfValidationException(String type1, String type2, JsonNode json) { + super("There are more than one matching types i.e. " + type1 + " and " + type2 + " on: " + json ); + } + + /** + * Initialization constructor. + * @param types List on unMapped types + * @param json Value that was not mapped by the above types + */ + public OneOfValidationException(List types, JsonNode json) { + super("We could not match any acceptable type from " + String.join(", ", types) + " on: " + json ); + } +} diff --git a/src/main/java/io/apimatic/core/utilities/CoreHelper.java b/src/main/java/io/apimatic/core/utilities/CoreHelper.java index ca2b36bd..fa5d936a 100644 --- a/src/main/java/io/apimatic/core/utilities/CoreHelper.java +++ b/src/main/java/io/apimatic/core/utilities/CoreHelper.java @@ -37,6 +37,7 @@ import javax.xml.transform.stream.StreamSource; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonToken; @@ -53,10 +54,13 @@ import com.fasterxml.jackson.databind.deser.std.StringDeserializer; import com.fasterxml.jackson.databind.exc.MismatchedInputException; import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder; import com.fasterxml.jackson.databind.module.SimpleModule; import io.apimatic.core.annotations.TypeCombinator.FormSerialize; import io.apimatic.core.annotations.TypeCombinator.TypeCombinatorCase; import io.apimatic.core.annotations.TypeCombinator.TypeCombinatorStringCase; +import io.apimatic.core.types.AnyOfValidationException; +import io.apimatic.core.types.OneOfValidationException; import io.apimatic.core.types.http.request.MultipartFileWrapper; import io.apimatic.core.types.http.request.MultipartWrapper; import io.apimatic.coreinterfaces.http.request.ArraySerializationFormat; @@ -66,1320 +70,1345 @@ */ public class CoreHelper { - /** - * A string of user agent. - */ - private static String userAgent; - - /** - * A tab separated array serialization format. - */ - private static final String TSV_FORMAT = "%09"; - - /** - * A comma separated array serialization format. - */ - private static final String CSV_FORMAT = ","; - - /** - * A pipe separated array serialization format - */ - private static final String PSV_FORMAT = "%7C"; - - /** - * Deserialization of Json data. - */ - private static ObjectMapper mapper = - JsonMapper.builder().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - .withConfigOverride(BigDecimal.class, - mutableConfigOverride -> mutableConfigOverride - .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING))) - .build(); - - /** - * Strict Deserialization of Json data. - */ - private static ObjectMapper strictMapper = - JsonMapper.builder().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - .configure(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES, true) - .configure(MapperFeature.ALLOW_COERCION_OF_SCALARS, false) - .addModule(new SimpleModule().addDeserializer(String.class, - new CoercionLessStringDeserializer())) - .withConfigOverride(BigDecimal.class, - mutableConfigOverride -> mutableConfigOverride - .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING))) - .build(); - - protected CoreHelper() {} - - - /** - * Get a JsonSerializer instance for a collection from the provided annotation. - * @param serializerAnnotation The Annotation containing information about the custom serializer - * of a collection. - * @return The JsonSerializer instance of the required type. - */ - private static JsonSerializer getCollectionCustomSerializer( - FormSerialize serializerAnnotation) { - try { - return serializerAnnotation.contentUsing().getDeclaredConstructor().newInstance(); - } catch (Exception e) { - return null; - } - } - - /** - * List of classes that are wrapped directly. This information is needed when traversing object - * trees for reference matching. - */ - private static final Set WRAPPER_TYPES = - new HashSet(Arrays.asList(Boolean.class, Character.class, Byte.class, - Short.class, String.class, Integer.class, Long.class, Float.class, Double.class, - BigDecimal.class, Void.class, File.class, MultipartWrapper.class, - MultipartFileWrapper.class)); - - /** - * Get a JsonSerializer instance from the provided annotation. - * @param serializerAnnotation The Annotation containing information about the serializer. - * @return The JsonSerializer instance of the required type. - */ - private static JsonSerializer getSerializer(JsonSerialize serializerAnnotation) { - try { - return serializerAnnotation.using().getDeclaredConstructor().newInstance(); - } catch (Exception e) { - return null; - } - } - - /** - * Get a JsonSerializer instance for a collection from the provided annotation. - * @param serializerAnnotation The Annotation containing information about the serializer of a - * collection. - * @return The JsonSerializer instance of the required type. - */ - private static JsonSerializer getCollectionSerializer(JsonSerialize serializerAnnotation) { - try { - return serializerAnnotation.contentUsing().getDeclaredConstructor().newInstance(); - } catch (Exception e) { - return null; - } - } - - - /** - * Deserialization of Json data. - * @return {@link ObjectMapper}. - */ - public static ObjectMapper getMapper() { - return mapper; - } - - - /** - * Strict Deserialization of Json data. - * @return {@link ObjectMapper}. - */ - public static ObjectMapper getStrictMapper() { - return strictMapper; - } - - /** - * Json Serialization of a given object. - * @param obj The object to serialize into Json. - * @return The serialized Json String representation of the given object. - * @throws JsonProcessingException Signals that a Json Processing Exception has occurred. - */ - public static String serialize(Object obj) throws JsonProcessingException { - if (obj == null) { - return null; - } - - return mapper.writeValueAsString(obj); - } - - /** - * Json Serialization of a given object using a specified JsonSerializer. - * @param obj The object to serialize into Json. - * @param serializer The instance of JsonSerializer to use. - * @return The serialized Json string representation of the given object. - * @throws JsonProcessingException Signals that a Json Processing Exception has occurred. - */ - @SuppressWarnings({"rawtypes", "unchecked"}) - public static String serialize(Object obj, final JsonSerializer serializer) - throws JsonProcessingException { - if (obj == null || serializer == null) { - return null; - } - - Class cls = null; - if (obj.getClass().getName().equals("java.util.ArrayList")) { - // need to find the generic type if it's an ArrayList - cls = ((ArrayList) obj).get(0).getClass(); - } else if (obj.getClass().getName().equals("java.util.LinkedHashMap")) { - cls = ((LinkedHashMap) obj).values().toArray()[0].getClass(); - } else { - cls = obj.getClass(); - } - - ObjectMapper objectMapper = new ObjectMapper(); - SimpleModule module = new SimpleModule(); - module.addSerializer(cls, serializer); - objectMapper.registerModule(module); - - return objectMapper.writeValueAsString(obj); - } - - - /** - * Xml Serialization of a given object list. - * @param Type of object to be serialized. - * @param objArray Object Array to be serialized. - * @param rootName Root name for the xml. - * @param nodeName Node name for the array nodes. - * @param cls Class of object to be serialized. - * @return The serialized Xml String representation of the given object array. - * @throws IOException Signals that an IO exception occurred. - */ - public static String serializeXmlArray( - T[] objArray, String rootName, String nodeName, Class cls) throws IOException { - try { - JAXBContext context = JAXBContext.newInstance(cls); - JAXBElement jaxbElement; - String xmlBlock = "<" + rootName + ">\n"; - for (T element : objArray) { - jaxbElement = new JAXBElement<>(new QName(nodeName), cls, element); - StringWriter writer = new StringWriter(); - Marshaller marshaller = context.createMarshaller(); - marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); - marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true); - marshaller.marshal(jaxbElement, writer); - xmlBlock += " " + writer.toString() + "\n"; - } - - xmlBlock += ""; - return xmlBlock; - } catch (JAXBException jaxbException) { - throw new IOException(jaxbException); - } - } - - /** - * Xml Serialization of a given object. - * @param Type of object to be serialized. - * @param obj Object to be serialized. - * @param rootName Root name for the xml. - * @param cls Class of object to be serialized. - * @return The serialized Xml String representation of the given object. - * @throws IOException Signals that an IOException exception occurred. - */ - public static String serializeXml(T obj, String rootName, Class cls) throws IOException { - try { - JAXBContext context = JAXBContext.newInstance(obj.getClass()); - JAXBElement elem = new JAXBElement<>(new QName(rootName), cls, obj); - - StringWriter writer = new StringWriter(); - Marshaller marshaller = context.createMarshaller(); - marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); - marshaller.marshal(elem, writer); - return writer.toString(); - } catch (JAXBException jaxbException) { - throw new IOException(jaxbException); - } - } - - - /** - * Json Serialization of a given container object based on annotation. - * @param obj The object to serialize into Json. - * @return The serialized Json String representation of the given object. - * @throws JsonProcessingException Signals that a Json Processing Exception has occurred. - */ - public static String serializeTypeCombinator(Object obj) throws JsonProcessingException { - if (obj == null) { - return null; - } - - Annotation stringCaseAnnotation = - obj.getClass().getAnnotation(TypeCombinatorStringCase.class); - - if (stringCaseAnnotation != null) { - return obj.toString(); - } - - return serialize(obj); - } - - /** - * Json deserialization of the given Json string using a specified JsonDerializer. - * @param jsonNode The JsonNode to deserialize. - * @param typeReference TypeReference of T1. - * @param The type of the object to deserialize into. - * @param The type of the custom deserializer. - * @param cls The class to attach the deserializer to. - * @param deserializer The deserializer to use. - * @return The deserialized object. - * @throws IOException Signals if any I/O exception occurred. - */ - public static T1 deserialize( - JsonNode jsonNode, final TypeReference typeReference, final Class cls, - final JsonDeserializer deserializer) throws IOException { - if (jsonNode == null) { - return null; - } - - return deserialize(mapper.writeValueAsString(jsonNode), typeReference, cls, deserializer); - } - - - /** - * Json deserialization of the given Json string using a specified JsonDerializer. - * @param json The Json string to deserialize. - * @param typeReference TypeReference of T1. - * @param The type of the object to deserialize into. - * @param The type of the custom deserializer. - * @param cls The class to attach the deserializer to. - * @param deserializer The deserializer to use. - * @return The deserialized object. - * @throws IOException Signals if any I/O exception occurred. - */ - public static T1 deserialize( - String json, final TypeReference typeReference, final Class cls, - final JsonDeserializer deserializer) throws IOException { - if (isNullOrWhiteSpace(json)) { - return null; - } - - return new ObjectMapper() { - private static final long serialVersionUID = -1639089569991988232L; - { - SimpleModule module = new SimpleModule(); - module.addDeserializer(cls, deserializer); - this.registerModule(module); - } - }.readValue(json, typeReference); - } - - /** - * Json deserialization of the given Json string. - * @param The type of the object to deserialize into. - * @param json The Json string to deserialize. - * @param clazz The type of the object to deserialize into. - * @return The deserialized object. - * @throws IOException Signals if any I/O exception occurred. - */ - public static T deserialize(String json, Class clazz) throws IOException { - if (isNullOrWhiteSpace(json)) { - return null; - } - - return mapper.readValue(json, clazz); - } - - /** - * Strict JSON deserialization of the given JSON string with FAIL_ON_UNKNOWN_PROPERTIES flag as - * true, used particularly for type combinators. - * @param The type of the object to deserialize into - * @param json The JsonNode to deserialize - * @param classes The list of types of the object to deserialize into - * @param isOneOf The boolean flag to validate for oneOf flow - * @return The deserialized object - * @throws IOException Signals if any I/O exception occurred. - */ - public static T deserialize( - JsonNode json, List> classes, boolean isOneOf) throws IOException { - if (json == null) { - return null; - } - - - T deserializedObject = null; - int deserializationCount = 0; - for (Class clazz : classes) { - try { - if (isOneOf) { - deserializedObject = strictMapper.convertValue(json, clazz); - deserializationCount++; - if (deserializationCount > 1) { - throw new IOException( - "More than 1 matching one-of types found against given json"); - } - } else { - return strictMapper.convertValue(json, clazz); - } - } catch (IllegalArgumentException e) { - // Ignoring the exception - } - } - if (deserializationCount == 0) { - throw new IOException("No " + (isOneOf ? "one-of" : "any-of") - + " type deserializer found against given json"); - } - - return deserializedObject; - } - - /** - * Json deserialization of the given Json string. - * @param json The Json string to deserialize. - * @return The deserialized Json as a Map. - * @throws IOException Signals if any I/O exception occurred. - */ - public static LinkedHashMap deserialize(String json) throws IOException { - if (isNullOrWhiteSpace(json)) { - return null; - } - - TypeReference> typeRef = - new TypeReference>() {}; - return deserialize(json, typeRef); - } - - /** - * JSON Deserialization of the given json string. - * @param json The json string to deserialize. - * @param typeReference TypeReference of T. - * @param The type of the object to deserialize into. - * @return The deserialized object. - * @throws IOException Signals if any I/O exception occurred. - */ - public static T deserialize(String json, TypeReference typeReference) - throws IOException { - if (isNullOrWhiteSpace(json)) { - return null; - } - - return mapper.readValue(json, typeReference); - } - - /** - * JSON Deserialization of the given json string with FAIL_ON_UNKNOWN_PROPERTIES flag as true. - * @param jsonNode The Json Node to deserialize. - * @param typeReference TypeReference of T. - * @param The type of the object to deserialize into. - * @return The deserialized object. - * @throws IOException Signals if any I/O exception occurred. - */ - public static T deserialize( - JsonNode jsonNode, TypeReference typeReference) throws IOException { - if (jsonNode == null) { - return null; - } - - return strictMapper.convertValue(jsonNode, typeReference); - } - - /** - * JSON deserialization of the given JsonNode with FAIL_ON_UNKNOWN_PROPERTIES flag as true. - * @param The type of the object to deserialize into. - * @param jsonNode The Json Node to deserialize. - * @param clazz The type of the object to deserialize into. - * @return The deserialized object. - * @throws IOException Signals if any I/O exception occurred. - */ - public static T deserialize(JsonNode jsonNode, Class clazz) - throws IOException { - if (jsonNode == null) { - return null; - } - - return strictMapper.convertValue(jsonNode, clazz); - } - - /** - * XML Deserialization of the given xml string. - * @param The class of the object to deserialize into. - * @param xml The xml string to deserialize. - * @param cls The class of the object to deserialize into. - * @return The deserialized object. - * @throws IOException Signals if any I/O exception occurred. - */ - public static T deserializeXml(String xml, Class cls) throws IOException { - try { - JAXBContext jaxbContext = JAXBContext.newInstance(cls); - Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); - StringReader reader = new StringReader(xml); - JAXBElement jaxbElement = jaxbUnmarshaller.unmarshal(new StreamSource(reader), cls); - - return jaxbElement.getValue(); - } catch (JAXBException jaxbException) { - throw new IOException(jaxbException); - } - } - - /** - * XML Deserialization of the given xml string. - * @param The class of the object to deserialize into. - * @param xml The xml string to deserialize. - * @param cls The class of the object to deserialize into. - * @return The deserialized object list. - * @throws IOException Signals if any I/O exception occurred. - */ - public static List deserializeXmlArray(String xml, Class cls) - throws IOException { - try { - JAXBContext jaxbContext = JAXBContext.newInstance(cls); - Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); - StringReader reader = new StringReader(xml); - JAXBElement jaxbElement = - jaxbUnmarshaller.unmarshal(new StreamSource(reader), cls); - - return Arrays.asList(jaxbElement.getValue()); - } catch (JAXBException jaxbException) { - throw new IOException(jaxbException); - } - } - - /** - * XML Deserialization of the given xml string for simple types. - * @param The class of the object to deserialize into. - * @param xml The xml string to deserialize. - * @param cls The class of the object to deserialize into. - * @return The deserialized simple types object list. - * @throws IOException Signals if any I/O exception occurred. - */ - public static List deserializeXmlSimpleTypesArray( - String xml, Class cls) throws IOException { - try { - JAXBContext jaxbContext = JAXBContext.newInstance(cls); - Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); - List deserializedList = new ArrayList<>(); - Pattern pattern = Pattern.compile("<.+?>(.+?)"); - Matcher patternMatcher = pattern.matcher(xml); - while (patternMatcher.find()) { - StringReader reader = new StringReader(patternMatcher.group()); - T unmarshalledElement = - jaxbUnmarshaller.unmarshal(new StreamSource(reader), cls).getValue(); - deserializedList.add(unmarshalledElement); - } - return deserializedList; - } catch (JAXBException jaxbException) { - throw new IOException(jaxbException); - } - } - - /** - * JSON Deserialization from custom deserializer based on given discriminator and registry. - * @param jp Parsed used for reading JSON content. - * @param ctxt Context that can be used to access information about this deserialization - * activity. - * @param discriminator The model's discriminator. - * @param registry The map containing all discriminators as keys and associated classes as - * values. - * @param typesWithoutDiscriminator The list containing all types without discriminators. - * @param isOneOf The boolean flag to validate for oneOf flow. - * @param the deserialized response type. - * @return The deserialized object. - * @throws IOException Signals if any I/O exception occurred. - */ - public static T deserialize( - JsonParser jp, DeserializationContext ctxt, String discriminator, - List>> registry, - List> typesWithoutDiscriminator, boolean isOneOf) - throws IOException { - ObjectCodec oc = jp.getCodec(); - JsonNode jsonNode = oc.readTree(jp); - List> types = deduceType(jsonNode, discriminator, registry); - - if (types == null || types.isEmpty()) { - if (typesWithoutDiscriminator != null && !typesWithoutDiscriminator.isEmpty()) { - types = typesWithoutDiscriminator; - } else { - throw new IOException("Discriminator is missing."); - } - } - - return deserialize(jsonNode, types, isOneOf); - } - - /** - * Json deserialization of the given Json string. - * @param json The Json string to deserialize. - * @return The deserialized Json as an Object. - */ - public static Object deserializeAsObject(String json) { - if (isNullOrWhiteSpace(json)) { - return null; - } - try { - return CoreHelper.deserialize(json, new TypeReference() {}); - } catch (IOException e) { - // Failed to deserialize when json is not representing a JSON object. - // i.e. either its string or any primitive type. - return json; - } - } - - /** - * JSON Deserialization of the given json string. - * @param The type of the object to deserialize into. - * @param json The Json string to deserialize. - * @param classArray The class of the array of objects to deserialize into. - * @return The deserialized list of objects. - * @throws IOException Signals if any I/O exception occurred.. - */ - public static List deserializeArray(String json, Class classArray) - throws IOException { - if (isNullOrWhiteSpace(json)) { - return null; - } - - return Arrays.asList(mapper.readValue(json, classArray)); - } - - - - /** - * Replaces template parameters in the given URL. - * @param queryBuilder The query string builder to replace the template parameters. - * @param parameters The parameters to replace in the URL. - */ - public static void appendUrlWithTemplateParameters( - StringBuilder queryBuilder, Map> parameters) { - // Perform parameter validation - if (null == queryBuilder) { - throw new IllegalArgumentException( - "Given value for parameter \"queryBuilder\" is invalid."); - } - - if (null == parameters) { - return; - } - - // Iterate and append parameters - for (Map.Entry> pair : parameters.entrySet()) { - - String replaceValue = ""; - Object element = pair.getValue().getKey(); - boolean shouldEncode = pair.getValue().getValue(); - - // Load element value as string - if (null == element) { - replaceValue = ""; - } else if (element instanceof Collection) { - replaceValue = - flattenCollection("", (Collection) element, shouldEncode, "%s%s%s", '/'); - } else { - if (shouldEncode) { - replaceValue = tryUrlEncode(element.toString(), false); - } else { - replaceValue = element.toString(); - } - } - - // Find the template parameter and replace it with its value - replaceAll(queryBuilder, "{" + pair.getKey() + "}", replaceValue); - } - } - - /** - * Appends the given set of parameters to the given query string. - * @param queryBuilder The query URL string to append the parameters. - * @param parameters The parameters to append. - * @param arraySerializationFormat the array serialization format. - */ - public static void appendUrlWithQueryParameters( - StringBuilder queryBuilder, Map parameters, - ArraySerializationFormat arraySerializationFormat) { - // Perform parameter validation - if (queryBuilder == null) { - throw new IllegalArgumentException( - "Given value for parameter \"queryBuilder\" is invalid."); - } - if (parameters == null || parameters.isEmpty()) { - return; - } - - // Check if query string already has parameters - boolean hasParams = queryBuilder.indexOf("?") > 0; - queryBuilder.append(hasParams ? '&' : '?'); - - encodeObjectAsQueryString("", parameters, queryBuilder, arraySerializationFormat); - } - - /** - * Validates if the string is null, empty or whitespace. - * @param s The string to validate. - * @return The result of validation. - */ - public static boolean isNullOrWhiteSpace(String s) { - if (s == null) { - return true; - } - - int length = s.length(); - if (length > 0) { - for (int start = 0, middle = length / 2, - end = length - 1; start <= middle; start++, end--) { - if (s.charAt(start) > ' ' || s.charAt(end) > ' ') { - return false; - } - } - return true; - } - return false; - } - - /** - * Replaces all occurrences of the given string in the string builder. - * @param stringBuilder The string builder to update with replaced strings. - * @param toReplace The string to replace in the string builder. - * @param replaceWith The string to replace with. - */ - public static void replaceAll( - StringBuilder stringBuilder, String toReplace, String replaceWith) { - int index = stringBuilder.indexOf(toReplace); - - while (index != -1) { - stringBuilder.replace(index, index + toReplace.length(), replaceWith); - index += replaceWith.length(); // Move to the end of the replacement - index = stringBuilder.indexOf(toReplace, index); - } - } - - /** - * Updates the user agent header value. - * @param apiUserAgent the String value of apiUserAgent. - * @param userAgentConfig the Map of user agent config. - * @return {@link String}. - */ - public static String updateUserAgent(String apiUserAgent, Map userAgentConfig) { - String engineVersion = System.getProperty("java.runtime.version"); - String osName = System.getProperty("os.name") + "-" + System.getProperty("os.version"); - userAgent = apiUserAgent; - userAgent = userAgent.replace("{engine}", "JRE"); - userAgent = - userAgent.replace("{engine-version}", engineVersion != null ? engineVersion : ""); - userAgent = userAgent.replace("{os-info}", osName != null ? osName : ""); - - if (userAgentConfig != null) { - userAgentConfig.forEach((key, value) -> { - userAgent = userAgent.replace(key, value); - }); - } - - return userAgent; - } - - /** - * Removes null values from the given map. - * @param map Map of values. - */ - public static void removeNullValues(Map map) { - if (map == null) { - return; - } - - map.values().removeAll(Collections.singleton(null)); - } - - /** - * Validates and processes the given URL. - * @param url The given URL to process. - * @return Pre-process URL as string. - */ - public static String cleanUrl(StringBuilder url) { - // Ensures that the URLs are absolute - Pattern pattern = Pattern.compile("^(https?://[^/]+)"); - Matcher matcher = pattern.matcher(url); - if (!matcher.find()) { - throw new IllegalArgumentException("Invalid Url format."); - } - - // Get the http protocol match - String protocol = matcher.group(1); - - // Removes redundant forward slashes - String query = url.substring(protocol.length()); - query = query.replaceAll("//+", "/"); - - // Returns processed URL - return protocol.concat(query); - } - - /** - * Prepares Array style form fields from a given array of values. - * @param value Value for the form fields. - * @param arraySerializationFormat serialization format. - * @return Dictionary of form fields created from array elements. - */ - public static List> prepareFormFields( - Map value, ArraySerializationFormat arraySerializationFormat) { - List> formFields = new ArrayList<>(); - if (value != null) { - objectToList("", value, formFields, new HashSet(), arraySerializationFormat); - } - return formFields; - } - - /** - * JSON Deserialization of the given json string with FAIL_ON_UNKNOWN_PROPERTIES flag as true. - * @param The type of the object to deserialize into. - * @param json The Json string to deserialize. - * @param classArray The class of the array of objects to deserialize into. - * @return The deserialized list of objects. - * @throws IOException Signals if any I/O exception occurred. - */ - public static List deserializeArray(JsonNode json, Class classArray) - throws IOException { - if (json == null) { - return null; - } - - return Arrays.asList(strictMapper.convertValue(json, classArray)); - } - - /** - * Deduces the type based on given discriminator and registry. - * @param jsonNode The json to check against. - * @param discriminator The model's discriminator. - * @param registry The Map containing all discriminators as keys and associated classes as - * values. - * @param The type of the object to deserialize into. - * @return The type to deserialize into. - * @throws IOException Signals if any I/O exception occurred. - */ - private static List> deduceType( - JsonNode jsonNode, String discriminator, List>> registry) - throws IOException { - if (jsonNode == null || registry == null) { - return null; - } - - final String discriminatorValue; - if (jsonNode.isArray()) { - if (jsonNode.has(0) && jsonNode.get(0).has(discriminator)) { - // JSON is an array of model objects - discriminatorValue = jsonNode.get(0).get(discriminator).asText(); - } else { - discriminatorValue = deduceTypeFromImmidiateChild(jsonNode.get(0), discriminator); - } - } else { - if (jsonNode.has(discriminator)) { - // JSON is a model object - discriminatorValue = jsonNode.get(discriminator).asText(); - } else { - // JSON is a Map so deduce discriminator for first child only - discriminatorValue = deduceTypeFromImmidiateChild(jsonNode, discriminator); - } - } - - return registry.stream().filter(item -> item.get(discriminatorValue) != null) - .map(item -> item.get(discriminatorValue)).distinct().collect(Collectors.toList()); - } - - /** - * Deduces the type from immediate child if exists based on given discriminator. - * @param jsonNode The json to check against. - * @param discriminator The model's discriminator. - * @return The type to deserialize into. - * @throws IOException Signals if any I/O exception occurred. - */ - private static String deduceTypeFromImmidiateChild(JsonNode jsonNode, String discriminator) { - Iterator iterator = jsonNode.iterator(); - while (iterator.hasNext()) { - JsonNode tempNode = iterator.next(); - if (tempNode.isArray()) { - if (tempNode.has(0) && tempNode.get(0).has(discriminator)) { - // JSON is an array of model objects - return tempNode.get(0).get(discriminator).asText(); - } - } else { - if (tempNode.has(discriminator)) { - // JSON is a model object - return tempNode.get(discriminator).asText(); - } - } - } - return null; - } - - /** - * Encodes a given object to URL encoded string. - * @param name Name of the object. - * @param obj Raw object sent from caller. - * @param objBuilder String of elements. - * @param arraySerializationFormat The array serialization format. - */ - private static void encodeObjectAsQueryString( - String name, Object obj, StringBuilder objBuilder, - ArraySerializationFormat arraySerializationFormat) { - - List> objectList = new ArrayList<>(); - objectToList(name, obj, objectList, new HashSet(), arraySerializationFormat); - boolean hasParam = false; - List arrays = new ArrayList(); - - for (SimpleEntry pair : objectList) { - String accessor = pair.getKey(); - // Ignore null - Object value = pair.getValue(); - if (value == null) { - continue; - } - - hasParam = true; - // Load element value as string - - - if (accessor.matches(".*?\\[\\d+\\]$") && isDelimeterFormat(arraySerializationFormat)) { - - String arrayName = accessor.substring(0, accessor.lastIndexOf('[')); - - if (arrays.contains(arrayName)) { - objBuilder.setLength(objBuilder.length() - 1); - accessor = getAccessorStringFormat(arraySerializationFormat); - } else { - accessor = arrayName + "="; - } - - if (!arrays.contains(arrayName)) { - arrays.add(arrayName); - } - - appendParamKeyValuePair("%s%s&", objBuilder, accessor, value); - } else { - appendParamKeyValuePair("%s=%s&", objBuilder, accessor, value); - } - } - - // Removing the last & - if (hasParam) { - objBuilder.setLength(objBuilder.length() - 1); - } - } - - private static void appendParamKeyValuePair( - String formatString, StringBuilder objBuilder, String accessor, Object value) { - - String paramKeyValPair = - String.format(formatString, accessor, tryUrlEncode(value.toString(), false)); - objBuilder.append(paramKeyValPair); - } - - /** - * Flattening a collection of objects into a string. - * @param elemName The element name of collection. - * @param array Array of elements to flatten. - * @param encode Need to encode?. - * @param fmt Format string to use for array flattening. - * @param separator Separator to use for string concatenation. - * @return Representative string made up of array elements. - */ - private static String flattenCollection( - String elemName, Collection array, boolean encode, String fmt, char separator) { - StringBuilder builder = new StringBuilder(); - - // Append all elements of the array into a string - for (Object element : array) { - String elemValue = null; - - // Replace null values with empty string to maintain index order - if (element == null) { - elemValue = ""; - } else { - elemValue = element.toString(); - } - if (encode) { - elemValue = tryUrlEncode(elemValue, false); - } - builder.append(String.format(fmt, elemName, elemValue, separator)); - } - - // Remove the last separator, if appended - if ((builder.length() > 1) && (builder.charAt(builder.length() - 1) == separator)) { - builder.deleteCharAt(builder.length() - 1); - } - - return builder.toString(); - } - - /** - * Tries URL encode using UTF-8. - * @param value The value to URL encode. - * @param spaceAsPercentEncoded The flag get space character as percent encoded. - * @return Encoded url. - */ - public static String tryUrlEncode(String value, boolean spaceAsPercentEncoded) { - try { - String encodedUrl = URLEncoder.encode(value, "UTF-8"); - if (spaceAsPercentEncoded) { - return encodedUrl.replace("+", "%20"); - } - return encodedUrl; - } catch (UnsupportedEncodingException ex) { - return value; - } - } - - /** - * Responsible to encode into base64 the username and password - * @param basicAuthUserName The auth username - * @param basicAuthPassword The auth password - * @return The base64 encoded String - */ - public static String getBase64EncodedCredentials( - String basicAuthUserName, String basicAuthPassword) { - String authCredentials = basicAuthUserName + ":" + basicAuthPassword; - return "Basic " + Base64.getEncoder().encodeToString(authCredentials.getBytes()); - } - - private static void objectToList( - String objName, Collection obj, List> objectList, - HashSet processed, ArraySerializationFormat arraySerializationFormat) { - - Collection array = obj; - // Append all elements of the array into a string - int index = 0; - for (Object element : array) { - // load key value pair - String key; - - if (isWrapperType(element) - && (arraySerializationFormat == ArraySerializationFormat.UNINDEXED - || arraySerializationFormat == ArraySerializationFormat.PLAIN)) { - key = - arraySerializationFormat == ArraySerializationFormat.UNINDEXED - ? String.format("%s[]", objName) - : objName; - } else { - key = String.format("%s[%d]", objName, index++); - } - loadKeyValuePairForEncoding(key, element, objectList, processed, - arraySerializationFormat); - } - - } - - private static void objectToList( - String objName, Map obj, List> objectList, - HashSet processed, ArraySerializationFormat arraySerializationFormat) { - // Process map - Map map = obj; - // Append all elements of the array into a string - for (Map.Entry pair : map.entrySet()) { - String attribName = pair.getKey().toString(); - if ((objName != null) && (!objName.isEmpty())) { - attribName = String.format("%s[%s]", objName, attribName); - } - loadKeyValuePairForEncoding(attribName, pair.getValue(), objectList, processed, - arraySerializationFormat); - } - } - - /** - * Converts a given object to a form encoded map. - * @param objName Name of the object. - * @param obj The object to convert into a map. - * @param objectList The object list to populate. - * @param processed List of object hashCodes that are already parsed. - * @param arraySerializationFormat The array serialization format. - */ - private static void objectToList( - String objName, Object obj, List> objectList, - HashSet processed, ArraySerializationFormat arraySerializationFormat) { - // Null values need not to be processed - if (obj == null) { - return; - } - - // Wrapper types are autoboxed, so reference checking is not needed - Class clazz = obj.getClass(); - - Annotation typeCombinatorAnnotation = clazz.getAnnotation(TypeCombinatorCase.class); - if (!isWrapperType(clazz) && typeCombinatorAnnotation == null) { - // Avoid infinite recursion - if (processed.contains(objName.hashCode())) { - return; - } - processed.add(objName.hashCode()); - } - - // Process arrays - if (obj instanceof Collection) { - objectToList(objName, (Collection) obj, objectList, processed, - arraySerializationFormat); - } else if (obj.getClass().isArray()) { - // Process array - - Object[] array = (Object[]) obj; - // Append all elements in the array into a string - int index = 0; - for (Object element : array) { - // Load key value pair - String key = String.format("%s[%d]", objName, index++); - loadKeyValuePairForEncoding(key, element, objectList, processed, - arraySerializationFormat); - } - } else if (obj instanceof Map) { - objectToList(objName, (Map) obj, objectList, processed, arraySerializationFormat); - } else { - // Process objects - if (typeCombinatorAnnotation != null) { - for (Field field : clazz.getDeclaredFields()) { - // unexpected field $jococoData came for unit test coverage and makes test fails - if (field.getName() == "$jacocoData") { - continue; - } - - Object fieldValue = null; - Annotation serializeAnnotation = null; - try { - field.setAccessible(true); - fieldValue = field.get(obj); - serializeAnnotation = field.getAnnotation(JsonSerialize.class); - if (serializeAnnotation == null) { - serializeAnnotation = field.getAnnotation(FormSerialize.class); - } - } catch (IllegalArgumentException | IllegalAccessException e) { - // Ignoring the exception - } - - if (serializeAnnotation != null) { - if (serializeAnnotation instanceof JsonSerialize) { - loadKeyValuePairForEncoding(objName, fieldValue, objectList, processed, - (JsonSerialize) serializeAnnotation, arraySerializationFormat); - } else { - loadKeyValuePairForEncoding(objName, fieldValue, objectList, processed, - (FormSerialize) serializeAnnotation, arraySerializationFormat); - } - } else { - loadKeyValuePairForEncoding(objName, fieldValue, objectList, processed, - arraySerializationFormat); - } - } - return; - } - // Invoke getter methods - while (clazz != null) { - for (Method method : clazz.getDeclaredMethods()) { - - // Is a public/protected getter or internalGetter? - if (method.getParameterTypes().length != 0 - || Modifier.isPrivate(method.getModifiers()) - || (!method.getName().startsWith("get") - && !method.getName().startsWith("internalGet"))) { - continue; - } - - // Get JsonGetter annotation - Annotation getterAnnotation = method.getAnnotation(JsonGetter.class); - if (getterAnnotation == null) { - continue; - } - - // Load key name from getter attribute name - String attribName = ((JsonGetter) getterAnnotation).value(); - if ((objName != null) && (!objName.isEmpty())) { - attribName = String.format("%s[%s]", objName, attribName); - } - - try { - // Load value by invoking getter method - method.setAccessible(true); - Object value = method.invoke(obj); - JsonSerialize serializerAnnotation = - method.getAnnotation(JsonSerialize.class); - // Load key value pair into objectList - if (serializerAnnotation != null) { - loadKeyValuePairForEncoding(attribName, value, objectList, processed, - serializerAnnotation, arraySerializationFormat); - } else { - loadKeyValuePairForEncoding(attribName, value, objectList, processed, - arraySerializationFormat); - } - } catch (IllegalAccessException | IllegalArgumentException - | InvocationTargetException e) { - // This block only calls getter methods. - // These getters don't throw any exception except invocationTargetException. - // The getters are public so there is no chance of an IllegalAccessException - // Steps we've followed ensure that the object has the specified method. - } - } - clazz = clazz.getSuperclass(); - } - } - } - - private static String getAccessorStringFormat( - ArraySerializationFormat arraySerializationFormat) { - switch (arraySerializationFormat) { - case CSV: - return CSV_FORMAT; - case PSV: - return PSV_FORMAT; - case TSV: - return TSV_FORMAT; - default: - return ""; - } - } - - private static boolean isDelimeterFormat(ArraySerializationFormat arraySerializationFormat) { - return (arraySerializationFormat == ArraySerializationFormat.CSV - || arraySerializationFormat == ArraySerializationFormat.TSV - || arraySerializationFormat == ArraySerializationFormat.PSV); - } - - /** - * Processes the value and load into objectList against key. - * @param key The key to used for creation of key value pair. - * @param value The value to process against the given key. - * @param objectList The object list to process with key value pair. - * @param processed List of processed objects hashCodes. - * @param serializer The serializer for serialize the object. - * @param arraySerializationFormat The array serialization format. - * @throws JsonProcessingException Signals that a Json Processing Exception has occurred. - */ - private static void loadKeyValueUsingSerializer( - String key, Object value, List> objectList, - HashSet processed, JsonSerializer serializer, - ArraySerializationFormat arraySerializationFormat) throws JsonProcessingException { - value = serialize(value, serializer); - - Object obj = deserializeAsObject(value.toString()); - if (obj instanceof List || obj instanceof Map) { - loadKeyValuePairForEncoding(key, obj, objectList, processed, arraySerializationFormat); - } else { - if (value.toString().startsWith("\"")) { - value = value.toString().substring(1, value.toString().length() - 1); - } - objectList.add(new SimpleEntry(key, value)); - } - } - - - /** - * While processing objects to map, loads value after serializing. - * @param key The key to used for creation of key value pair. - * @param value The value to process against the given key. - * @param objectList The object list to process with key value pair. - * @param processed List of processed objects hashCodes. - * @param formSerializerAnnotation Annotation for serializer. - * @param arraySerializationFormat The array serialization format. - */ - private static void loadKeyValuePairForEncoding( - String key, Object value, List> objectList, - HashSet processed, FormSerialize formSerializerAnnotation, - ArraySerializationFormat arraySerializationFormat) { - if (value == null) { - return; - } - - try { - JsonSerializer serializer = getCollectionCustomSerializer(formSerializerAnnotation); - loadKeyValueUsingSerializer(key, value, objectList, processed, serializer, - arraySerializationFormat); - } catch (JsonProcessingException e) { - e.printStackTrace(); - } - } - - - /** - * While processing objects to map, decides whether to perform recursion or load value. - * @param key The key for creating key value pair. - * @param value The value to process against the given key. - * @param objectList The object list to process with key value pair. - * @param processed List of processed objects hashCodes. - * @param arraySerializationFormat The array serialization format. - */ - private static void loadKeyValuePairForEncoding( - String key, Object value, List> objectList, - HashSet processed, ArraySerializationFormat arraySerializationFormat) { - if (value == null) { - return; - } - if (isWrapperType(value)) { - objectList.add(new SimpleEntry(key, value)); - } else if (value instanceof CoreJsonObject) { - objectToList(key, ((CoreJsonObject) value).getStoredObject(), objectList, processed, - arraySerializationFormat); - } else if (value instanceof CoreJsonValue) { - Object storedValue = ((CoreJsonValue) value).getStoredObject(); - if (isWrapperType(storedValue)) { - objectList.add(new SimpleEntry(key, storedValue)); - } else { - objectToList(key, storedValue, objectList, processed, arraySerializationFormat); - } - } else if (value instanceof UUID) { - // UUIDs can be converted to string - objectList.add(new SimpleEntry(key, value.toString())); - } else { - objectToList(key, value, objectList, processed, arraySerializationFormat); - } - } - - /** - * While processing objects to map, loads value after serializing. - * @param key The key to used for creation of key value pair. - * @param value The value to process against the given key. - * @param objectList The object list to process with key value pair. - * @param processed List of processed objects hashCodes. - * @param serializerAnnotation Annotation for serializer. - * @param arraySerializationFormat The array serialization format. - */ - private static void loadKeyValuePairForEncoding( - String key, Object value, List> objectList, - HashSet processed, JsonSerialize serializerAnnotation, - ArraySerializationFormat arraySerializationFormat) { - if (value == null) { - return; - } - - try { - JsonSerializer serializer = getSerializer(serializerAnnotation); - if (serializer == null) { - serializer = getCollectionSerializer(serializerAnnotation); - } - - loadKeyValueUsingSerializer(key, value, objectList, processed, serializer, - arraySerializationFormat); - } catch (JsonProcessingException e) { - e.printStackTrace(); - } - } - - /** - * Check if the given object can be wrapped directly. - * @param object The given object. - * @return true if the class is an autoboxed class e.g., Integer. - */ - private static boolean isWrapperType(Object object) { - if (object == null) { - return false; - } - return WRAPPER_TYPES.contains(object.getClass()) || object.getClass().isPrimitive() - || object.getClass().isEnum(); - } - - /** - * Json Serialization of an ENUM defined under oneOf/anyOf container. - * @param value The object to serialize into Json String. - * @return The serialized Json String representation of the given object. - * @throws JsonProcessingException Signals that a Json Processing Exception has occurred. - */ - public static String serializeEnumContainer(Object value) throws JsonProcessingException { - if (value instanceof String || value instanceof Integer) { - return String.valueOf(value); - } - - return serialize(value); - } - - /** - * Custom deserializer class of any string property for disallowing implicit type conversion. - */ - private static class CoercionLessStringDeserializer extends StringDeserializer { - private static final long serialVersionUID = 1L; - - @Override - public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - - if (p.getCurrentToken() != JsonToken.VALUE_STRING) { - String message = "Cannot coerce " + p.getCurrentToken() + " to String value"; - throw MismatchedInputException.from(p, String.class, message); - } - return super.deserialize(p, ctxt); - } - } + /** + * A string of user agent. + */ + private static String userAgent; + + /** + * A tab separated array serialization format. + */ + private static final String TSV_FORMAT = "%09"; + + /** + * A comma separated array serialization format. + */ + private static final String CSV_FORMAT = ","; + + /** + * A pipe separated array serialization format + */ + private static final String PSV_FORMAT = "%7C"; + + /** + * Deserialization of Json data. + */ + private static ObjectMapper mapper = JsonMapper.builder() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .withConfigOverride(BigDecimal.class, mutableConfigOverride -> mutableConfigOverride + .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING))) + .build(); + + /** + * Strict Deserialization of Json data. + */ + private static ObjectMapper strictMapper = JsonMapper.builder() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .configure(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES, true) + .configure(MapperFeature.ALLOW_COERCION_OF_SCALARS, false) + .addModule(new SimpleModule().addDeserializer(String.class, new CoercionLessStringDeserializer())) + .withConfigOverride(BigDecimal.class, mutableConfigOverride -> mutableConfigOverride + .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING))) + .build(); + + protected CoreHelper() { + } + + /** + * Get a JsonSerializer instance for a collection from the provided annotation. + * + * @param serializerAnnotation The Annotation containing information about the + * custom serializer of a collection. + * @return The JsonSerializer instance of the required type. + */ + private static JsonSerializer getCollectionCustomSerializer(FormSerialize serializerAnnotation) { + try { + return serializerAnnotation.contentUsing().getDeclaredConstructor().newInstance(); + } catch (Exception e) { + return null; + } + } + + /** + * List of classes that are wrapped directly. This information is needed when + * traversing object trees for reference matching. + */ + private static final Set WRAPPER_TYPES = new HashSet(Arrays.asList(Boolean.class, Character.class, + Byte.class, Short.class, String.class, Integer.class, Long.class, Float.class, Double.class, + BigDecimal.class, Void.class, File.class, MultipartWrapper.class, MultipartFileWrapper.class)); + + /** + * Get a JsonSerializer instance from the provided annotation. + * + * @param serializerAnnotation The Annotation containing information about the + * serializer. + * @return The JsonSerializer instance of the required type. + */ + private static JsonSerializer getSerializer(JsonSerialize serializerAnnotation) { + try { + return serializerAnnotation.using().getDeclaredConstructor().newInstance(); + } catch (Exception e) { + return null; + } + } + + /** + * Get a JsonSerializer instance for a collection from the provided annotation. + * + * @param serializerAnnotation The Annotation containing information about the + * serializer of a collection. + * @return The JsonSerializer instance of the required type. + */ + private static JsonSerializer getCollectionSerializer(JsonSerialize serializerAnnotation) { + try { + return serializerAnnotation.contentUsing().getDeclaredConstructor().newInstance(); + } catch (Exception e) { + return null; + } + } + + /** + * Deserialization of Json data. + * + * @return {@link ObjectMapper}. + */ + public static ObjectMapper getMapper() { + return mapper; + } + + /** + * Strict Deserialization of Json data. + * + * @return {@link ObjectMapper}. + */ + public static ObjectMapper getStrictMapper() { + return strictMapper; + } + + /** + * Json Serialization of a given object. + * + * @param obj The object to serialize into Json. + * @return The serialized Json String representation of the given object. + * @throws JsonProcessingException Signals that a Json Processing Exception has + * occurred. + */ + public static String serialize(Object obj) throws JsonProcessingException { + if (obj == null) { + return null; + } + + return mapper.writeValueAsString(obj); + } + + /** + * Json Serialization of a given object using a specified JsonSerializer. + * + * @param obj The object to serialize into Json. + * @param serializer The instance of JsonSerializer to use. + * @return The serialized Json string representation of the given object. + * @throws JsonProcessingException Signals that a Json Processing Exception has + * occurred. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static String serialize(Object obj, final JsonSerializer serializer) throws JsonProcessingException { + if (obj == null || serializer == null) { + return null; + } + + Class cls = null; + if (obj.getClass().getName().equals("java.util.ArrayList")) { + // need to find the generic type if it's an ArrayList + cls = ((ArrayList) obj).get(0).getClass(); + } else if (obj.getClass().getName().equals("java.util.LinkedHashMap")) { + cls = ((LinkedHashMap) obj).values().toArray()[0].getClass(); + } else { + cls = obj.getClass(); + } + + ObjectMapper objectMapper = new ObjectMapper(); + SimpleModule module = new SimpleModule(); + module.addSerializer(cls, serializer); + objectMapper.registerModule(module); + + return objectMapper.writeValueAsString(obj); + } + + /** + * Xml Serialization of a given object list. + * + * @param Type of object to be serialized. + * @param objArray Object Array to be serialized. + * @param rootName Root name for the xml. + * @param nodeName Node name for the array nodes. + * @param cls Class of object to be serialized. + * @return The serialized Xml String representation of the given object array. + * @throws IOException Signals that an IO exception occurred. + */ + public static String serializeXmlArray(T[] objArray, String rootName, String nodeName, Class cls) + throws IOException { + try { + JAXBContext context = JAXBContext.newInstance(cls); + JAXBElement jaxbElement; + String xmlBlock = "<" + rootName + ">\n"; + for (T element : objArray) { + jaxbElement = new JAXBElement<>(new QName(nodeName), cls, element); + StringWriter writer = new StringWriter(); + Marshaller marshaller = context.createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true); + marshaller.marshal(jaxbElement, writer); + xmlBlock += " " + writer.toString() + "\n"; + } + + xmlBlock += ""; + return xmlBlock; + } catch (JAXBException jaxbException) { + throw new IOException(jaxbException); + } + } + + /** + * Xml Serialization of a given object. + * + * @param Type of object to be serialized. + * @param obj Object to be serialized. + * @param rootName Root name for the xml. + * @param cls Class of object to be serialized. + * @return The serialized Xml String representation of the given object. + * @throws IOException Signals that an IOException exception occurred. + */ + public static String serializeXml(T obj, String rootName, Class cls) throws IOException { + try { + JAXBContext context = JAXBContext.newInstance(obj.getClass()); + JAXBElement elem = new JAXBElement<>(new QName(rootName), cls, obj); + + StringWriter writer = new StringWriter(); + Marshaller marshaller = context.createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + marshaller.marshal(elem, writer); + return writer.toString(); + } catch (JAXBException jaxbException) { + throw new IOException(jaxbException); + } + } + + /** + * Json Serialization of a given container object based on annotation. + * + * @param obj The object to serialize into Json. + * @return The serialized Json String representation of the given object. + * @throws JsonProcessingException Signals that a Json Processing Exception has + * occurred. + */ + public static String serializeTypeCombinator(Object obj) throws JsonProcessingException { + if (obj == null) { + return null; + } + + Annotation stringCaseAnnotation = obj.getClass().getAnnotation(TypeCombinatorStringCase.class); + + if (stringCaseAnnotation != null) { + return obj.toString(); + } + + return serialize(obj); + } + + /** + * Json deserialization of the given Json string using a specified + * JsonDerializer. + * + * @param jsonNode The JsonNode to deserialize. + * @param typeReference TypeReference of T1. + * @param The type of the object to deserialize into. + * @param The type of the custom deserializer. + * @param cls The class to attach the deserializer to. + * @param deserializer The deserializer to use. + * @return The deserialized object. + * @throws IOException Signals if any I/O exception occurred. + */ + public static T1 deserialize(JsonNode jsonNode, + final TypeReference typeReference, final Class cls, final JsonDeserializer deserializer) + throws IOException { + if (jsonNode == null) { + return null; + } + + return deserialize(mapper.writeValueAsString(jsonNode), typeReference, cls, deserializer); + } + + /** + * Json deserialization of the given Json string using a specified + * JsonDerializer. + * + * @param json The Json string to deserialize. + * @param typeReference TypeReference of T1. + * @param The type of the object to deserialize into. + * @param The type of the custom deserializer. + * @param cls The class to attach the deserializer to. + * @param deserializer The deserializer to use. + * @return The deserialized object. + * @throws IOException Signals if any I/O exception occurred. + */ + public static T1 deserialize(String json, + final TypeReference typeReference, final Class cls, final JsonDeserializer deserializer) + throws IOException { + if (isNullOrWhiteSpace(json)) { + return null; + } + + return new ObjectMapper() { + private static final long serialVersionUID = -1639089569991988232L; + { + SimpleModule module = new SimpleModule(); + module.addDeserializer(cls, deserializer); + this.registerModule(module); + } + }.readValue(json, typeReference); + } + + /** + * Json deserialization of the given Json string. + * + * @param The type of the object to deserialize into. + * @param json The Json string to deserialize. + * @param clazz The type of the object to deserialize into. + * @return The deserialized object. + * @throws IOException Signals if any I/O exception occurred. + */ + public static T deserialize(String json, Class clazz) throws IOException { + if (isNullOrWhiteSpace(json)) { + return null; + } + + return mapper.readValue(json, clazz); + } + + /** + * Strict JSON deserialization of the given JSON string with + * FAIL_ON_UNKNOWN_PROPERTIES flag as true, used particularly for type + * combinators. + * + * @param The type of the object to deserialize into + * @param json The JsonNode to deserialize + * @param classes The list of types of the object to deserialize into + * @param isOneOf The boolean flag to validate for oneOf flow + * @return The deserialized object + * @throws IOException Signals if any I/O exception occurred. + */ + public static T deserialize(JsonNode json, List> classes, boolean isOneOf) + throws IOException { + if (json == null) { + return null; + } + + T deserializedObject = null; + String mappedType = null; + List unMappedTypes = new ArrayList(); + boolean matchFound = false; + for (Class clazz : classes) { + String classType = getTypeCombinatorCaseType(clazz); + try { + deserializedObject = strictMapper.convertValue(json, clazz); + if (isOneOf && matchFound) { + throw new OneOfValidationException(mappedType, classType, json); + } + mappedType = classType; + matchFound = true; + if (!isOneOf) { + break; + } + } catch (IllegalArgumentException e) { + unMappedTypes.add(classType); + } + } + if (matchFound) { + return deserializedObject; + } + if (isOneOf) { + throw new OneOfValidationException(unMappedTypes, json); + } + throw new AnyOfValidationException(unMappedTypes, json); + } + + private static String getTypeCombinatorCaseType(Class clazz) { + TypeCombinatorCase caseAnnotation = clazz.getAnnotation(TypeCombinatorCase.class); + if (caseAnnotation == null) { + return ""; + } + return caseAnnotation.type(); + } + + /** + * Json deserialization of the given Json string. + * + * @param json The Json string to deserialize. + * @return The deserialized Json as a Map. + * @throws IOException Signals if any I/O exception occurred. + */ + public static LinkedHashMap deserialize(String json) throws IOException { + if (isNullOrWhiteSpace(json)) { + return null; + } + + TypeReference> typeRef = new TypeReference>() { + }; + return deserialize(json, typeRef); + } + + /** + * JSON Deserialization of the given json string. + * + * @param json The json string to deserialize. + * @param typeReference TypeReference of T. + * @param The type of the object to deserialize into. + * @return The deserialized object. + * @throws IOException Signals if any I/O exception occurred. + */ + public static T deserialize(String json, TypeReference typeReference) throws IOException { + if (isNullOrWhiteSpace(json)) { + return null; + } + + return mapper.readValue(json, typeReference); + } + + /** + * JSON Deserialization of the given json string with FAIL_ON_UNKNOWN_PROPERTIES + * flag as true. + * + * @param jsonNode The Json Node to deserialize. + * @param typeReference TypeReference of T. + * @param The type of the object to deserialize into. + * @return The deserialized object. + * @throws IOException Signals if any I/O exception occurred. + */ + public static T deserialize(JsonNode jsonNode, TypeReference typeReference) + throws IOException { + if (jsonNode == null) { + return null; + } + + return strictMapper.convertValue(jsonNode, typeReference); + } + + /** + * JSON deserialization of the given JsonNode with FAIL_ON_UNKNOWN_PROPERTIES + * flag as true. + * + * @param The type of the object to deserialize into. + * @param jsonNode The Json Node to deserialize. + * @param clazz The type of the object to deserialize into. + * @return The deserialized object. + * @throws IOException Signals if any I/O exception occurred. + */ + public static T deserialize(JsonNode jsonNode, Class clazz) throws IOException { + if (jsonNode == null) { + return null; + } + return strictMapper.convertValue(jsonNode, clazz); + } + + /** + * XML Deserialization of the given xml string. + * + * @param The class of the object to deserialize into. + * @param xml The xml string to deserialize. + * @param cls The class of the object to deserialize into. + * @return The deserialized object. + * @throws IOException Signals if any I/O exception occurred. + */ + public static T deserializeXml(String xml, Class cls) throws IOException { + try { + JAXBContext jaxbContext = JAXBContext.newInstance(cls); + Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); + StringReader reader = new StringReader(xml); + JAXBElement jaxbElement = jaxbUnmarshaller.unmarshal(new StreamSource(reader), cls); + + return jaxbElement.getValue(); + } catch (JAXBException jaxbException) { + throw new IOException(jaxbException); + } + } + + /** + * XML Deserialization of the given xml string. + * + * @param The class of the object to deserialize into. + * @param xml The xml string to deserialize. + * @param cls The class of the object to deserialize into. + * @return The deserialized object list. + * @throws IOException Signals if any I/O exception occurred. + */ + public static List deserializeXmlArray(String xml, Class cls) throws IOException { + try { + JAXBContext jaxbContext = JAXBContext.newInstance(cls); + Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); + StringReader reader = new StringReader(xml); + JAXBElement jaxbElement = jaxbUnmarshaller.unmarshal(new StreamSource(reader), cls); + + return Arrays.asList(jaxbElement.getValue()); + } catch (JAXBException jaxbException) { + throw new IOException(jaxbException); + } + } + + /** + * XML Deserialization of the given xml string for simple types. + * + * @param The class of the object to deserialize into. + * @param xml The xml string to deserialize. + * @param cls The class of the object to deserialize into. + * @return The deserialized simple types object list. + * @throws IOException Signals if any I/O exception occurred. + */ + public static List deserializeXmlSimpleTypesArray(String xml, Class cls) + throws IOException { + try { + JAXBContext jaxbContext = JAXBContext.newInstance(cls); + Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); + List deserializedList = new ArrayList<>(); + Pattern pattern = Pattern.compile("<.+?>(.+?)"); + Matcher patternMatcher = pattern.matcher(xml); + while (patternMatcher.find()) { + StringReader reader = new StringReader(patternMatcher.group()); + T unmarshalledElement = jaxbUnmarshaller.unmarshal(new StreamSource(reader), cls).getValue(); + deserializedList.add(unmarshalledElement); + } + return deserializedList; + } catch (JAXBException jaxbException) { + throw new IOException(jaxbException); + } + } + + /** + * JSON Deserialization from custom deserializer based on given discriminator + * and registry. + * + * @param jp Parsed used for reading JSON content. + * @param ctxt Context that can be used to access + * information about this deserialization + * activity. + * @param discriminator The model's discriminator. + * @param registry The map containing all discriminators as + * keys and associated classes as values. + * @param typesWithoutDiscriminator The list containing all types without + * discriminators. + * @param isOneOf The boolean flag to validate for oneOf flow. + * @param the deserialized response type. + * @return The deserialized object. + * @throws IOException Signals if any I/O exception occurred. + */ + public static T deserialize(JsonParser jp, DeserializationContext ctxt, String discriminator, + List>> registry, List> typesWithoutDiscriminator, + boolean isOneOf) throws IOException { + ObjectCodec oc = jp.getCodec(); + JsonNode jsonNode = oc.readTree(jp); + List> types = deduceType(jsonNode, discriminator, registry); + + if (types == null || types.isEmpty()) { + if (typesWithoutDiscriminator != null && !typesWithoutDiscriminator.isEmpty()) { + types = typesWithoutDiscriminator; + } else { + throw new IOException("Discriminator is missing."); + } + } + + return deserialize(jsonNode, types, isOneOf); + } + + /** + * Json deserialization of the given Json string. + * + * @param json The Json string to deserialize. + * @return The deserialized Json as an Object. + */ + public static Object deserializeAsObject(String json) { + if (isNullOrWhiteSpace(json)) { + return null; + } + try { + return CoreHelper.deserialize(json, new TypeReference() { + }); + } catch (IOException e) { + // Failed to deserialize when json is not representing a JSON object. + // i.e. either its string or any primitive type. + return json; + } + } + + /** + * JSON Deserialization of the given json string. + * + * @param The type of the object to deserialize into. + * @param json The Json string to deserialize. + * @param classArray The class of the array of objects to deserialize into. + * @return The deserialized list of objects. + * @throws IOException Signals if any I/O exception occurred.. + */ + public static List deserializeArray(String json, Class classArray) throws IOException { + if (isNullOrWhiteSpace(json)) { + return null; + } + + return Arrays.asList(mapper.readValue(json, classArray)); + } + + /** + * Replaces template parameters in the given URL. + * + * @param queryBuilder The query string builder to replace the template + * parameters. + * @param parameters The parameters to replace in the URL. + */ + public static void appendUrlWithTemplateParameters(StringBuilder queryBuilder, + Map> parameters) { + // Perform parameter validation + if (null == queryBuilder) { + throw new IllegalArgumentException("Given value for parameter \"queryBuilder\" is invalid."); + } + + if (null == parameters) { + return; + } + + // Iterate and append parameters + for (Map.Entry> pair : parameters.entrySet()) { + + String replaceValue = ""; + Object element = pair.getValue().getKey(); + boolean shouldEncode = pair.getValue().getValue(); + + // Load element value as string + if (null == element) { + replaceValue = ""; + } else if (element instanceof Collection) { + replaceValue = flattenCollection("", (Collection) element, shouldEncode, "%s%s%s", '/'); + } else { + if (shouldEncode) { + replaceValue = tryUrlEncode(element.toString(), false); + } else { + replaceValue = element.toString(); + } + } + + // Find the template parameter and replace it with its value + replaceAll(queryBuilder, "{" + pair.getKey() + "}", replaceValue); + } + } + + /** + * Appends the given set of parameters to the given query string. + * + * @param queryBuilder The query URL string to append the + * parameters. + * @param parameters The parameters to append. + * @param arraySerializationFormat the array serialization format. + */ + public static void appendUrlWithQueryParameters(StringBuilder queryBuilder, Map parameters, + ArraySerializationFormat arraySerializationFormat) { + // Perform parameter validation + if (queryBuilder == null) { + throw new IllegalArgumentException("Given value for parameter \"queryBuilder\" is invalid."); + } + if (parameters == null || parameters.isEmpty()) { + return; + } + + // Check if query string already has parameters + boolean hasParams = queryBuilder.indexOf("?") > 0; + queryBuilder.append(hasParams ? '&' : '?'); + + encodeObjectAsQueryString("", parameters, queryBuilder, arraySerializationFormat); + } + + /** + * Validates if the string is null, empty or whitespace. + * + * @param s The string to validate. + * @return The result of validation. + */ + public static boolean isNullOrWhiteSpace(String s) { + if (s == null) { + return true; + } + + int length = s.length(); + if (length > 0) { + for (int start = 0, middle = length / 2, end = length - 1; start <= middle; start++, end--) { + if (s.charAt(start) > ' ' || s.charAt(end) > ' ') { + return false; + } + } + return true; + } + return false; + } + + /** + * Replaces all occurrences of the given string in the string builder. + * + * @param stringBuilder The string builder to update with replaced strings. + * @param toReplace The string to replace in the string builder. + * @param replaceWith The string to replace with. + */ + public static void replaceAll(StringBuilder stringBuilder, String toReplace, String replaceWith) { + int index = stringBuilder.indexOf(toReplace); + + while (index != -1) { + stringBuilder.replace(index, index + toReplace.length(), replaceWith); + index += replaceWith.length(); // Move to the end of the replacement + index = stringBuilder.indexOf(toReplace, index); + } + } + + /** + * Updates the user agent header value. + * + * @param apiUserAgent the String value of apiUserAgent. + * @param userAgentConfig the Map of user agent config. + * @return {@link String}. + */ + public static String updateUserAgent(String apiUserAgent, Map userAgentConfig) { + String engineVersion = System.getProperty("java.runtime.version"); + String osName = System.getProperty("os.name") + "-" + System.getProperty("os.version"); + userAgent = apiUserAgent; + userAgent = userAgent.replace("{engine}", "JRE"); + userAgent = userAgent.replace("{engine-version}", engineVersion != null ? engineVersion : ""); + userAgent = userAgent.replace("{os-info}", osName != null ? osName : ""); + + if (userAgentConfig != null) { + userAgentConfig.forEach((key, value) -> { + userAgent = userAgent.replace(key, value); + }); + } + + return userAgent; + } + + /** + * Removes null values from the given map. + * + * @param map Map of values. + */ + public static void removeNullValues(Map map) { + if (map == null) { + return; + } + + map.values().removeAll(Collections.singleton(null)); + } + + /** + * Validates and processes the given URL. + * + * @param url The given URL to process. + * @return Pre-process URL as string. + */ + public static String cleanUrl(StringBuilder url) { + // Ensures that the URLs are absolute + Pattern pattern = Pattern.compile("^(https?://[^/]+)"); + Matcher matcher = pattern.matcher(url); + if (!matcher.find()) { + throw new IllegalArgumentException("Invalid Url format."); + } + + // Get the http protocol match + String protocol = matcher.group(1); + + // Removes redundant forward slashes + String query = url.substring(protocol.length()); + query = query.replaceAll("//+", "/"); + + // Returns processed URL + return protocol.concat(query); + } + + /** + * Prepares Array style form fields from a given array of values. + * + * @param value Value for the form fields. + * @param arraySerializationFormat serialization format. + * @return Dictionary of form fields created from array elements. + */ + public static List> prepareFormFields(Map value, + ArraySerializationFormat arraySerializationFormat) { + List> formFields = new ArrayList<>(); + if (value != null) { + objectToList("", value, formFields, new HashSet(), arraySerializationFormat); + } + return formFields; + } + + /** + * JSON Deserialization of the given json string with FAIL_ON_UNKNOWN_PROPERTIES + * flag as true. + * + * @param The type of the object to deserialize into. + * @param json The Json string to deserialize. + * @param classArray The class of the array of objects to deserialize into. + * @return The deserialized list of objects. + * @throws IOException Signals if any I/O exception occurred. + */ + public static List deserializeArray(JsonNode json, Class classArray) throws IOException { + if (json == null) { + return null; + } + + return Arrays.asList(strictMapper.convertValue(json, classArray)); + } + + /** + * Deduces the type based on given discriminator and registry. + * + * @param jsonNode The json to check against. + * @param discriminator The model's discriminator. + * @param registry The Map containing all discriminators as keys and + * associated classes as values. + * @param The type of the object to deserialize into. + * @return The type to deserialize into. + * @throws IOException Signals if any I/O exception occurred. + */ + private static List> deduceType(JsonNode jsonNode, String discriminator, + List>> registry) throws IOException { + if (jsonNode == null || registry == null) { + return null; + } + + final String discriminatorValue; + if (jsonNode.isArray()) { + if (jsonNode.has(0) && jsonNode.get(0).has(discriminator)) { + // JSON is an array of model objects + discriminatorValue = jsonNode.get(0).get(discriminator).asText(); + } else { + discriminatorValue = deduceTypeFromImmidiateChild(jsonNode.get(0), discriminator); + } + } else { + if (jsonNode.has(discriminator)) { + // JSON is a model object + discriminatorValue = jsonNode.get(discriminator).asText(); + } else { + // JSON is a Map so deduce discriminator for first child only + discriminatorValue = deduceTypeFromImmidiateChild(jsonNode, discriminator); + } + } + + return registry.stream().filter(item -> item.get(discriminatorValue) != null) + .map(item -> item.get(discriminatorValue)).distinct().collect(Collectors.toList()); + } + + /** + * Deduces the type from immediate child if exists based on given discriminator. + * + * @param jsonNode The json to check against. + * @param discriminator The model's discriminator. + * @return The type to deserialize into. + * @throws IOException Signals if any I/O exception occurred. + */ + private static String deduceTypeFromImmidiateChild(JsonNode jsonNode, String discriminator) { + Iterator iterator = jsonNode.iterator(); + while (iterator.hasNext()) { + JsonNode tempNode = iterator.next(); + if (tempNode.isArray()) { + if (tempNode.has(0) && tempNode.get(0).has(discriminator)) { + // JSON is an array of model objects + return tempNode.get(0).get(discriminator).asText(); + } + } else { + if (tempNode.has(discriminator)) { + // JSON is a model object + return tempNode.get(discriminator).asText(); + } + } + } + return null; + } + + /** + * Encodes a given object to URL encoded string. + * + * @param name Name of the object. + * @param obj Raw object sent from caller. + * @param objBuilder String of elements. + * @param arraySerializationFormat The array serialization format. + */ + private static void encodeObjectAsQueryString(String name, Object obj, StringBuilder objBuilder, + ArraySerializationFormat arraySerializationFormat) { + + List> objectList = new ArrayList<>(); + objectToList(name, obj, objectList, new HashSet(), arraySerializationFormat); + boolean hasParam = false; + List arrays = new ArrayList(); + + for (SimpleEntry pair : objectList) { + String accessor = pair.getKey(); + // Ignore null + Object value = pair.getValue(); + if (value == null) { + continue; + } + + hasParam = true; + // Load element value as string + + if (accessor.matches(".*?\\[\\d+\\]$") && isDelimeterFormat(arraySerializationFormat)) { + + String arrayName = accessor.substring(0, accessor.lastIndexOf('[')); + + if (arrays.contains(arrayName)) { + objBuilder.setLength(objBuilder.length() - 1); + accessor = getAccessorStringFormat(arraySerializationFormat); + } else { + accessor = arrayName + "="; + } + + if (!arrays.contains(arrayName)) { + arrays.add(arrayName); + } + + appendParamKeyValuePair("%s%s&", objBuilder, accessor, value); + } else { + appendParamKeyValuePair("%s=%s&", objBuilder, accessor, value); + } + } + + // Removing the last & + if (hasParam) { + objBuilder.setLength(objBuilder.length() - 1); + } + } + + private static void appendParamKeyValuePair(String formatString, StringBuilder objBuilder, String accessor, + Object value) { + + String paramKeyValPair = String.format(formatString, accessor, tryUrlEncode(value.toString(), false)); + objBuilder.append(paramKeyValPair); + } + + /** + * Flattening a collection of objects into a string. + * + * @param elemName The element name of collection. + * @param array Array of elements to flatten. + * @param encode Need to encode?. + * @param fmt Format string to use for array flattening. + * @param separator Separator to use for string concatenation. + * @return Representative string made up of array elements. + */ + private static String flattenCollection(String elemName, Collection array, boolean encode, String fmt, + char separator) { + StringBuilder builder = new StringBuilder(); + + // Append all elements of the array into a string + for (Object element : array) { + String elemValue = null; + + // Replace null values with empty string to maintain index order + if (element == null) { + elemValue = ""; + } else { + elemValue = element.toString(); + } + if (encode) { + elemValue = tryUrlEncode(elemValue, false); + } + builder.append(String.format(fmt, elemName, elemValue, separator)); + } + + // Remove the last separator, if appended + if ((builder.length() > 1) && (builder.charAt(builder.length() - 1) == separator)) { + builder.deleteCharAt(builder.length() - 1); + } + + return builder.toString(); + } + + /** + * Tries URL encode using UTF-8. + * + * @param value The value to URL encode. + * @param spaceAsPercentEncoded The flag get space character as percent encoded. + * @return Encoded url. + */ + public static String tryUrlEncode(String value, boolean spaceAsPercentEncoded) { + try { + String encodedUrl = URLEncoder.encode(value, "UTF-8"); + if (spaceAsPercentEncoded) { + return encodedUrl.replace("+", "%20"); + } + return encodedUrl; + } catch (UnsupportedEncodingException ex) { + return value; + } + } + + /** + * Responsible to encode into base64 the username and password + * + * @param basicAuthUserName The auth username + * @param basicAuthPassword The auth password + * @return The base64 encoded String + */ + public static String getBase64EncodedCredentials(String basicAuthUserName, String basicAuthPassword) { + String authCredentials = basicAuthUserName + ":" + basicAuthPassword; + return "Basic " + Base64.getEncoder().encodeToString(authCredentials.getBytes()); + } + + private static void objectToList(String objName, Collection obj, List> objectList, + HashSet processed, ArraySerializationFormat arraySerializationFormat) { + + Collection array = obj; + // Append all elements of the array into a string + int index = 0; + for (Object element : array) { + // load key value pair + String key; + + if (isWrapperType(element) && (arraySerializationFormat == ArraySerializationFormat.UNINDEXED + || arraySerializationFormat == ArraySerializationFormat.PLAIN)) { + key = arraySerializationFormat == ArraySerializationFormat.UNINDEXED ? String.format("%s[]", objName) + : objName; + } else { + key = String.format("%s[%d]", objName, index++); + } + loadKeyValuePairForEncoding(key, element, objectList, processed, arraySerializationFormat); + } + + } + + private static void objectToList(String objName, Map obj, List> objectList, + HashSet processed, ArraySerializationFormat arraySerializationFormat) { + // Process map + Map map = obj; + // Append all elements of the array into a string + for (Map.Entry pair : map.entrySet()) { + String attribName = pair.getKey().toString(); + if ((objName != null) && (!objName.isEmpty())) { + attribName = String.format("%s[%s]", objName, attribName); + } + loadKeyValuePairForEncoding(attribName, pair.getValue(), objectList, processed, arraySerializationFormat); + } + } + + /** + * Converts a given object to a form encoded map. + * + * @param objName Name of the object. + * @param obj The object to convert into a map. + * @param objectList The object list to populate. + * @param processed List of object hashCodes that are already + * parsed. + * @param arraySerializationFormat The array serialization format. + */ + private static void objectToList(String objName, Object obj, List> objectList, + HashSet processed, ArraySerializationFormat arraySerializationFormat) { + // Null values need not to be processed + if (obj == null) { + return; + } + + // Wrapper types are autoboxed, so reference checking is not needed + Class clazz = obj.getClass(); + + Annotation typeCombinatorAnnotation = clazz.getAnnotation(TypeCombinatorCase.class); + if (!isWrapperType(clazz) && typeCombinatorAnnotation == null) { + // Avoid infinite recursion + if (processed.contains(objName.hashCode())) { + return; + } + processed.add(objName.hashCode()); + } + + // Process arrays + if (obj instanceof Collection) { + objectToList(objName, (Collection) obj, objectList, processed, arraySerializationFormat); + } else if (obj.getClass().isArray()) { + // Process array + + Object[] array = (Object[]) obj; + // Append all elements in the array into a string + int index = 0; + for (Object element : array) { + // Load key value pair + String key = String.format("%s[%d]", objName, index++); + loadKeyValuePairForEncoding(key, element, objectList, processed, arraySerializationFormat); + } + } else if (obj instanceof Map) { + objectToList(objName, (Map) obj, objectList, processed, arraySerializationFormat); + } else { + // Process objects + if (typeCombinatorAnnotation != null) { + for (Field field : clazz.getDeclaredFields()) { + // unexpected field $jococoData came for unit test coverage and makes test fails + if (field.getName() == "$jacocoData") { + continue; + } + + Object fieldValue = null; + Annotation serializeAnnotation = null; + try { + field.setAccessible(true); + fieldValue = field.get(obj); + serializeAnnotation = field.getAnnotation(JsonSerialize.class); + if (serializeAnnotation == null) { + serializeAnnotation = field.getAnnotation(FormSerialize.class); + } + } catch (IllegalArgumentException | IllegalAccessException e) { + // Ignoring the exception + } + + if (serializeAnnotation != null) { + if (serializeAnnotation instanceof JsonSerialize) { + loadKeyValuePairForEncoding(objName, fieldValue, objectList, processed, + (JsonSerialize) serializeAnnotation, arraySerializationFormat); + } else { + loadKeyValuePairForEncoding(objName, fieldValue, objectList, processed, + (FormSerialize) serializeAnnotation, arraySerializationFormat); + } + } else { + loadKeyValuePairForEncoding(objName, fieldValue, objectList, processed, + arraySerializationFormat); + } + } + return; + } + // Invoke getter methods + while (clazz != null) { + for (Method method : clazz.getDeclaredMethods()) { + + // Is a public/protected getter or internalGetter? + if (method.getParameterTypes().length != 0 || Modifier.isPrivate(method.getModifiers()) + || (!method.getName().startsWith("get") && !method.getName().startsWith("internalGet"))) { + continue; + } + + // Get JsonGetter annotation + Annotation getterAnnotation = method.getAnnotation(JsonGetter.class); + if (getterAnnotation == null) { + continue; + } + + // Load key name from getter attribute name + String attribName = ((JsonGetter) getterAnnotation).value(); + if ((objName != null) && (!objName.isEmpty())) { + attribName = String.format("%s[%s]", objName, attribName); + } + + try { + // Load value by invoking getter method + method.setAccessible(true); + Object value = method.invoke(obj); + JsonSerialize serializerAnnotation = method.getAnnotation(JsonSerialize.class); + // Load key value pair into objectList + if (serializerAnnotation != null) { + loadKeyValuePairForEncoding(attribName, value, objectList, processed, serializerAnnotation, + arraySerializationFormat); + } else { + loadKeyValuePairForEncoding(attribName, value, objectList, processed, + arraySerializationFormat); + } + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + // This block only calls getter methods. + // These getters don't throw any exception except invocationTargetException. + // The getters are public so there is no chance of an IllegalAccessException + // Steps we've followed ensure that the object has the specified method. + } + } + clazz = clazz.getSuperclass(); + } + } + } + + private static String getAccessorStringFormat(ArraySerializationFormat arraySerializationFormat) { + switch (arraySerializationFormat) { + case CSV: + return CSV_FORMAT; + case PSV: + return PSV_FORMAT; + case TSV: + return TSV_FORMAT; + default: + return ""; + } + } + + private static boolean isDelimeterFormat(ArraySerializationFormat arraySerializationFormat) { + return (arraySerializationFormat == ArraySerializationFormat.CSV + || arraySerializationFormat == ArraySerializationFormat.TSV + || arraySerializationFormat == ArraySerializationFormat.PSV); + } + + /** + * Processes the value and load into objectList against key. + * + * @param key The key to used for creation of key value + * pair. + * @param value The value to process against the given key. + * @param objectList The object list to process with key value + * pair. + * @param processed List of processed objects hashCodes. + * @param serializer The serializer for serialize the object. + * @param arraySerializationFormat The array serialization format. + * @throws JsonProcessingException Signals that a Json Processing Exception has + * occurred. + */ + private static void loadKeyValueUsingSerializer(String key, Object value, + List> objectList, HashSet processed, JsonSerializer serializer, + ArraySerializationFormat arraySerializationFormat) throws JsonProcessingException { + value = serialize(value, serializer); + + Object obj = deserializeAsObject(value.toString()); + if (obj instanceof List || obj instanceof Map) { + loadKeyValuePairForEncoding(key, obj, objectList, processed, arraySerializationFormat); + } else { + if (value.toString().startsWith("\"")) { + value = value.toString().substring(1, value.toString().length() - 1); + } + objectList.add(new SimpleEntry(key, value)); + } + } + + /** + * While processing objects to map, loads value after serializing. + * + * @param key The key to used for creation of key value + * pair. + * @param value The value to process against the given key. + * @param objectList The object list to process with key value + * pair. + * @param processed List of processed objects hashCodes. + * @param formSerializerAnnotation Annotation for serializer. + * @param arraySerializationFormat The array serialization format. + */ + private static void loadKeyValuePairForEncoding(String key, Object value, + List> objectList, HashSet processed, + FormSerialize formSerializerAnnotation, ArraySerializationFormat arraySerializationFormat) { + if (value == null) { + return; + } + + try { + JsonSerializer serializer = getCollectionCustomSerializer(formSerializerAnnotation); + loadKeyValueUsingSerializer(key, value, objectList, processed, serializer, arraySerializationFormat); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + + /** + * While processing objects to map, decides whether to perform recursion or load + * value. + * + * @param key The key for creating key value pair. + * @param value The value to process against the given key. + * @param objectList The object list to process with key value + * pair. + * @param processed List of processed objects hashCodes. + * @param arraySerializationFormat The array serialization format. + */ + private static void loadKeyValuePairForEncoding(String key, Object value, + List> objectList, HashSet processed, + ArraySerializationFormat arraySerializationFormat) { + if (value == null) { + return; + } + if (isWrapperType(value)) { + objectList.add(new SimpleEntry(key, value)); + } else if (value instanceof CoreJsonObject) { + objectToList(key, ((CoreJsonObject) value).getStoredObject(), objectList, processed, + arraySerializationFormat); + } else if (value instanceof CoreJsonValue) { + Object storedValue = ((CoreJsonValue) value).getStoredObject(); + if (isWrapperType(storedValue)) { + objectList.add(new SimpleEntry(key, storedValue)); + } else { + objectToList(key, storedValue, objectList, processed, arraySerializationFormat); + } + } else if (value instanceof UUID) { + // UUIDs can be converted to string + objectList.add(new SimpleEntry(key, value.toString())); + } else { + objectToList(key, value, objectList, processed, arraySerializationFormat); + } + } + + /** + * While processing objects to map, loads value after serializing. + * + * @param key The key to used for creation of key value + * pair. + * @param value The value to process against the given key. + * @param objectList The object list to process with key value + * pair. + * @param processed List of processed objects hashCodes. + * @param serializerAnnotation Annotation for serializer. + * @param arraySerializationFormat The array serialization format. + */ + private static void loadKeyValuePairForEncoding(String key, Object value, + List> objectList, HashSet processed, + JsonSerialize serializerAnnotation, ArraySerializationFormat arraySerializationFormat) { + if (value == null) { + return; + } + + try { + JsonSerializer serializer = getSerializer(serializerAnnotation); + if (serializer == null) { + serializer = getCollectionSerializer(serializerAnnotation); + } + + loadKeyValueUsingSerializer(key, value, objectList, processed, serializer, arraySerializationFormat); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + + /** + * Check if the given object can be wrapped directly. + * + * @param object The given object. + * @return true if the class is an autoboxed class e.g., Integer. + */ + private static boolean isWrapperType(Object object) { + if (object == null) { + return false; + } + return WRAPPER_TYPES.contains(object.getClass()) || object.getClass().isPrimitive() + || object.getClass().isEnum(); + } + + /** + * Json Serialization of an ENUM defined under oneOf/anyOf container. + * + * @param value The object to serialize into Json String. + * @return The serialized Json String representation of the given object. + * @throws JsonProcessingException Signals that a Json Processing Exception has + * occurred. + */ + public static String serializeEnumContainer(Object value) throws JsonProcessingException { + if (value instanceof String || value instanceof Integer) { + return String.valueOf(value); + } + + return serialize(value); + } + + /** + * Custom deserializer class of any string property for disallowing implicit + * type conversion. + */ + private static class CoercionLessStringDeserializer extends StringDeserializer { + private static final long serialVersionUID = 1L; + + @Override + public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + + if (p.getCurrentToken() != JsonToken.VALUE_STRING) { + String message = "Cannot coerce " + p.getCurrentToken() + " to String value"; + throw MismatchedInputException.from(p, String.class, message); + } + return super.deserialize(p, ctxt); + } + } } diff --git a/src/test/java/apimatic/core/models/AtomCase.java b/src/test/java/apimatic/core/models/AtomCase.java index b951f6cc..cc496799 100644 --- a/src/test/java/apimatic/core/models/AtomCase.java +++ b/src/test/java/apimatic/core/models/AtomCase.java @@ -15,7 +15,7 @@ * This is a implementation class for AtomCase. */ @JsonDeserialize(using = JsonDeserializer.None.class) -@TypeCombinatorCase +@TypeCombinatorCase(type = "Map") public class AtomCase { @JsonValue diff --git a/src/test/java/apimatic/core/models/OrbitCase.java b/src/test/java/apimatic/core/models/OrbitCase.java index 923e6935..41ab7bab 100644 --- a/src/test/java/apimatic/core/models/OrbitCase.java +++ b/src/test/java/apimatic/core/models/OrbitCase.java @@ -13,7 +13,7 @@ * This is a implementation class for OrbitCase. */ @JsonDeserialize(using = JsonDeserializer.None.class) -@TypeCombinatorCase +@TypeCombinatorCase(type = "Orbit") public class OrbitCase { @JsonValue diff --git a/src/test/java/apimatic/core/utilities/CoreHelperTest.java b/src/test/java/apimatic/core/utilities/CoreHelperTest.java index 8c400dcc..2e82baa5 100644 --- a/src/test/java/apimatic/core/utilities/CoreHelperTest.java +++ b/src/test/java/apimatic/core/utilities/CoreHelperTest.java @@ -41,6 +41,8 @@ import apimatic.core.models.Person; import apimatic.core.models.containers.SendParamsFormDateTime; import apimatic.core.models.containers.SendScalarParamBody; +import io.apimatic.core.types.AnyOfValidationException; +import io.apimatic.core.types.OneOfValidationException; import io.apimatic.core.utilities.CoreHelper; import io.apimatic.core.utilities.CoreJsonObject; import io.apimatic.core.utilities.CoreJsonValue; @@ -968,31 +970,31 @@ public void testDeserializeNullJson() throws IOException { } @Test - public void testDeserializeOneOf() throws IOException { - String json = - "{\"key1\":{\"NumberOfElectrons\":2,\"NumberOfProtons\":2}," - + "\"key2\":{\"NumberOfElectrons\":2,\"NumberOfProtons\":2}}"; - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode jsonNode = objectMapper.readTree(json); - + public void testDeserializeOneOfNull() throws IOException { + JsonNode jsonNode = null; Object result = CoreHelper.deserialize(jsonNode, Arrays.asList(AtomCase.class, OrbitCase.class), true); - assertNotNull(result); + assertNull(result); } @Test - public void testDeserializeOneOfNull() throws IOException { - JsonNode jsonNode = null; + public void testDeserializeOneOfA() throws IOException { + String json = + "{\"key1\":{\"NumberOfElectrons\":2,\"NumberOfProtons\":2}," + + "\"key2\":{\"NumberOfElectrons\":2,\"NumberOfProtons\":2}}"; + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode jsonNode = objectMapper.readTree(json); + Object result = CoreHelper.deserialize(jsonNode, Arrays.asList(AtomCase.class, OrbitCase.class), true); - assertNull(result); + assertEquals("{key1=Atom [numberOfElectrons=2, numberOfProtons=2], " + + "key2=Atom [numberOfElectrons=2, numberOfProtons=2]}", result.toString()); } - @Test - public void testDeserializeOneOf1() throws IOException { + public void testDeserializeOneOfB() throws IOException { String json = "{\"NumberOfElectrons\":2}"; ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonNode = objectMapper.readTree(json); @@ -1000,31 +1002,39 @@ public void testDeserializeOneOf1() throws IOException { Object result = CoreHelper.deserialize(jsonNode, Arrays.asList(AtomCase.class, OrbitCase.class), true); - assertNotNull(result); + assertEquals("Orbit [numberOfElectrons=2]", result.toString()); } - @Test(expected = IOException.class) - public void testDeserializeOneOf2() throws IOException { + @Test(expected = OneOfValidationException.class) + public void testDeserializeOneOfFailA() throws IOException { String json = "{\"RandomKey\":2}"; ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonNode = objectMapper.readTree(json); - - CoreHelper.deserialize(jsonNode, Arrays.asList(AtomCase.class, OrbitCase.class), true); + try { + CoreHelper.deserialize(jsonNode, Arrays.asList(AtomCase.class, OrbitCase.class), true); + } catch (Exception e) { + assertEquals("We could not match any acceptable type from " + + "Map, Orbit on: {\"RandomKey\":2}", e.getMessage()); + throw e; + } } - @Test(expected = IOException.class) - public void testDeserializeOneOf3() throws IOException { - String json = - "[{\"NumberOfElectrons\":2},{\"NumberOfElectrons\":2, \"NumberOfProtons\":2}]"; + @Test(expected = OneOfValidationException.class) + public void testDeserializeOneOfFailB() throws IOException { + String json = "{\"NumberOfElectrons\":2}"; ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonNode = objectMapper.readTree(json); - - CoreHelper.deserialize(jsonNode, Arrays.asList(AtomCase.class, OrbitCase.class), true); + try { + CoreHelper.deserialize(jsonNode, Arrays.asList(OrbitCase.class, OrbitCase.class), true); + } catch (Exception e) { + assertEquals("There are more than one matching types i.e. Orbit and " + + "Orbit on: {\"NumberOfElectrons\":2}", e.getMessage()); + throw e; + } } - @Test - public void testDeserializeAnyOf() throws IOException { + public void testDeserializeAnyOfA() throws IOException { String json = "{\"key1\":{\"NumberOfElectrons\":2,\"NumberOfProtons\":2}," + "\"key2\":{\"NumberOfElectrons\":2,\"NumberOfProtons\":2}}"; @@ -1034,11 +1044,12 @@ public void testDeserializeAnyOf() throws IOException { Object result = CoreHelper.deserialize(jsonNode, Arrays.asList(AtomCase.class, OrbitCase.class), false); - assertNotNull(result); + assertEquals("{key1=Atom [numberOfElectrons=2, numberOfProtons=2], " + + "key2=Atom [numberOfElectrons=2, numberOfProtons=2]}", result.toString()); } @Test - public void testDeserializeOneOfAnyOf1() throws IOException { + public void testDeserializeAnyOfB() throws IOException { String json = "{\"NumberOfElectrons\":2}"; ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonNode = objectMapper.readTree(json); @@ -1046,29 +1057,39 @@ public void testDeserializeOneOfAnyOf1() throws IOException { Object result = CoreHelper.deserialize(jsonNode, Arrays.asList(AtomCase.class, OrbitCase.class), false); - assertNotNull(result); + assertEquals("Orbit [numberOfElectrons=2]", result.toString()); } - @Test(expected = IOException.class) - public void testDeserializeOneOfAnyOf2() throws IOException { + @Test(expected = AnyOfValidationException.class) + public void testDeserializeAnyOfFailA() throws IOException { String json = "{\"RandomKey\":2}"; ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonNode = objectMapper.readTree(json); - - CoreHelper.deserialize(jsonNode, Arrays.asList(AtomCase.class, OrbitCase.class), false); + try { + CoreHelper.deserialize(jsonNode, Arrays.asList(AtomCase.class, OrbitCase.class), false); + } catch (Exception e) { + assertEquals("We could not match any acceptable type from " + + "Map, Orbit on: {\"RandomKey\":2}", e.getMessage()); + throw e; + } } - @Test(expected = IOException.class) - public void testDeserializeAnyOf3() throws IOException { + @Test(expected = AnyOfValidationException.class) + public void testDeserializeAnyOfFailB() throws IOException { String json = "[{\"NumberOfElectrons\":2},{\"NumberOfElectrons\":2, \"NumberOfProtons\":2}]"; ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonNode = objectMapper.readTree(json); - - CoreHelper.deserialize(jsonNode, Arrays.asList(AtomCase.class, OrbitCase.class), false); + try { + CoreHelper.deserialize(jsonNode, Arrays.asList(AtomCase.class, OrbitCase.class), false); + } catch (Exception e) { + assertEquals("We could not match any acceptable type " + + "from Map, Orbit on: [{\"NumberOfElectrons\":2}," + + "{\"NumberOfElectrons\":2,\"NumberOfProtons\":2}]", e.getMessage()); + throw e; + } } - @Test public void testTypeCombinatorSerializationString() throws JsonProcessingException { SendScalarParamBody body = SendScalarParamBody.fromMString("some string"); From 4fbcf49271bd1f0e90c48a912e81a74c8622ae50 Mon Sep 17 00:00:00 2001 From: Asad Ali Date: Wed, 24 May 2023 10:16:12 +0500 Subject: [PATCH 2/4] fixed linting issues --- .../core/annotations/TypeCombinator.java | 6 +- .../core/types/AnyOfValidationException.java | 21 +- .../core/types/OneOfValidationException.java | 26 +- .../apimatic/core/utilities/CoreHelper.java | 2724 +++++++++-------- .../core/utilities/CoreHelperTest.java | 467 ++- 5 files changed, 1623 insertions(+), 1621 deletions(-) diff --git a/src/main/java/io/apimatic/core/annotations/TypeCombinator.java b/src/main/java/io/apimatic/core/annotations/TypeCombinator.java index 08180c91..1c02b977 100644 --- a/src/main/java/io/apimatic/core/annotations/TypeCombinator.java +++ b/src/main/java/io/apimatic/core/annotations/TypeCombinator.java @@ -14,7 +14,11 @@ public interface TypeCombinator { @Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @interface TypeCombinatorCase { - public String type() default ""; + /** + * Java type of the object wrapped in the annotated TypeCombinator container. + * @return String encoded java type of the wrapped object + */ + String type() default ""; } @Retention(RetentionPolicy.RUNTIME) diff --git a/src/main/java/io/apimatic/core/types/AnyOfValidationException.java b/src/main/java/io/apimatic/core/types/AnyOfValidationException.java index 541a5e94..e48d093c 100644 --- a/src/main/java/io/apimatic/core/types/AnyOfValidationException.java +++ b/src/main/java/io/apimatic/core/types/AnyOfValidationException.java @@ -9,17 +9,18 @@ */ public class AnyOfValidationException extends IOException { - /** - * UID for serialization. - */ - private static final long serialVersionUID = 1214174253911720228L; + /** + * UID for serialization. + */ + private static final long serialVersionUID = 1214174253911720228L; - /** + /** * Initialization constructor. - * @param types List on unMapped types - * @param json Value that was not mapped by the above types - */ - public AnyOfValidationException(List types, JsonNode json) { - super("We could not match any acceptable type from " + String.join(", ", types) + " on: " + json ); + * @param types List on unMapped types + * @param json Value that was not mapped by the above types + */ + public AnyOfValidationException(final List types, final JsonNode json) { + super("We could not match any acceptable type from " + + String.join(", ", types) + " on: " + json); } } diff --git a/src/main/java/io/apimatic/core/types/OneOfValidationException.java b/src/main/java/io/apimatic/core/types/OneOfValidationException.java index 3f6c13a9..ceddd037 100644 --- a/src/main/java/io/apimatic/core/types/OneOfValidationException.java +++ b/src/main/java/io/apimatic/core/types/OneOfValidationException.java @@ -9,10 +9,10 @@ */ public class OneOfValidationException extends IOException { - /** - * UID for serialization. - */ - private static final long serialVersionUID = 6424174253911720228L; + /** + * UID for serialization. + */ + private static final long serialVersionUID = 6424174253911720228L; /** * Initialization constructor. @@ -20,16 +20,18 @@ public class OneOfValidationException extends IOException { * @param type2 The second type that also got mapped on jsonNode * @param json Value that was mapped by the above two types */ - public OneOfValidationException(String type1, String type2, JsonNode json) { - super("There are more than one matching types i.e. " + type1 + " and " + type2 + " on: " + json ); + public OneOfValidationException(final String type1, final String type2, final JsonNode json) { + super("There are more than one matching types i.e. " + + type1 + " and " + type2 + " on: " + json); } - /** + /** * Initialization constructor. - * @param types List on unMapped types - * @param json Value that was not mapped by the above types - */ - public OneOfValidationException(List types, JsonNode json) { - super("We could not match any acceptable type from " + String.join(", ", types) + " on: " + json ); + * @param types List on unMapped types + * @param json Value that was not mapped by the above types + */ + public OneOfValidationException(final List types, final JsonNode json) { + super("We could not match any acceptable type from " + + String.join(", ", types) + " on: " + json); } } diff --git a/src/main/java/io/apimatic/core/utilities/CoreHelper.java b/src/main/java/io/apimatic/core/utilities/CoreHelper.java index fa5d936a..17254e59 100644 --- a/src/main/java/io/apimatic/core/utilities/CoreHelper.java +++ b/src/main/java/io/apimatic/core/utilities/CoreHelper.java @@ -37,7 +37,6 @@ import javax.xml.transform.stream.StreamSource; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonGetter; -import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonToken; @@ -54,7 +53,6 @@ import com.fasterxml.jackson.databind.deser.std.StringDeserializer; import com.fasterxml.jackson.databind.exc.MismatchedInputException; import com.fasterxml.jackson.databind.json.JsonMapper; -import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder; import com.fasterxml.jackson.databind.module.SimpleModule; import io.apimatic.core.annotations.TypeCombinator.FormSerialize; import io.apimatic.core.annotations.TypeCombinator.TypeCombinatorCase; @@ -70,1345 +68,1385 @@ */ public class CoreHelper { - /** - * A string of user agent. - */ - private static String userAgent; - - /** - * A tab separated array serialization format. - */ - private static final String TSV_FORMAT = "%09"; - - /** - * A comma separated array serialization format. - */ - private static final String CSV_FORMAT = ","; - - /** - * A pipe separated array serialization format - */ - private static final String PSV_FORMAT = "%7C"; - - /** - * Deserialization of Json data. - */ - private static ObjectMapper mapper = JsonMapper.builder() - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - .withConfigOverride(BigDecimal.class, mutableConfigOverride -> mutableConfigOverride - .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING))) - .build(); - - /** - * Strict Deserialization of Json data. - */ - private static ObjectMapper strictMapper = JsonMapper.builder() - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - .configure(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES, true) - .configure(MapperFeature.ALLOW_COERCION_OF_SCALARS, false) - .addModule(new SimpleModule().addDeserializer(String.class, new CoercionLessStringDeserializer())) - .withConfigOverride(BigDecimal.class, mutableConfigOverride -> mutableConfigOverride - .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING))) - .build(); - - protected CoreHelper() { - } - - /** - * Get a JsonSerializer instance for a collection from the provided annotation. - * - * @param serializerAnnotation The Annotation containing information about the - * custom serializer of a collection. - * @return The JsonSerializer instance of the required type. - */ - private static JsonSerializer getCollectionCustomSerializer(FormSerialize serializerAnnotation) { - try { - return serializerAnnotation.contentUsing().getDeclaredConstructor().newInstance(); - } catch (Exception e) { - return null; - } - } - - /** - * List of classes that are wrapped directly. This information is needed when - * traversing object trees for reference matching. - */ - private static final Set WRAPPER_TYPES = new HashSet(Arrays.asList(Boolean.class, Character.class, - Byte.class, Short.class, String.class, Integer.class, Long.class, Float.class, Double.class, - BigDecimal.class, Void.class, File.class, MultipartWrapper.class, MultipartFileWrapper.class)); - - /** - * Get a JsonSerializer instance from the provided annotation. - * - * @param serializerAnnotation The Annotation containing information about the - * serializer. - * @return The JsonSerializer instance of the required type. - */ - private static JsonSerializer getSerializer(JsonSerialize serializerAnnotation) { - try { - return serializerAnnotation.using().getDeclaredConstructor().newInstance(); - } catch (Exception e) { - return null; - } - } - - /** - * Get a JsonSerializer instance for a collection from the provided annotation. - * - * @param serializerAnnotation The Annotation containing information about the - * serializer of a collection. - * @return The JsonSerializer instance of the required type. - */ - private static JsonSerializer getCollectionSerializer(JsonSerialize serializerAnnotation) { - try { - return serializerAnnotation.contentUsing().getDeclaredConstructor().newInstance(); - } catch (Exception e) { - return null; - } - } - - /** - * Deserialization of Json data. - * - * @return {@link ObjectMapper}. - */ - public static ObjectMapper getMapper() { - return mapper; - } - - /** - * Strict Deserialization of Json data. - * - * @return {@link ObjectMapper}. - */ - public static ObjectMapper getStrictMapper() { - return strictMapper; - } - - /** - * Json Serialization of a given object. - * - * @param obj The object to serialize into Json. - * @return The serialized Json String representation of the given object. - * @throws JsonProcessingException Signals that a Json Processing Exception has - * occurred. - */ - public static String serialize(Object obj) throws JsonProcessingException { - if (obj == null) { - return null; - } - - return mapper.writeValueAsString(obj); - } - - /** - * Json Serialization of a given object using a specified JsonSerializer. - * - * @param obj The object to serialize into Json. - * @param serializer The instance of JsonSerializer to use. - * @return The serialized Json string representation of the given object. - * @throws JsonProcessingException Signals that a Json Processing Exception has - * occurred. - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - public static String serialize(Object obj, final JsonSerializer serializer) throws JsonProcessingException { - if (obj == null || serializer == null) { - return null; - } - - Class cls = null; - if (obj.getClass().getName().equals("java.util.ArrayList")) { - // need to find the generic type if it's an ArrayList - cls = ((ArrayList) obj).get(0).getClass(); - } else if (obj.getClass().getName().equals("java.util.LinkedHashMap")) { - cls = ((LinkedHashMap) obj).values().toArray()[0].getClass(); - } else { - cls = obj.getClass(); - } - - ObjectMapper objectMapper = new ObjectMapper(); - SimpleModule module = new SimpleModule(); - module.addSerializer(cls, serializer); - objectMapper.registerModule(module); - - return objectMapper.writeValueAsString(obj); - } - - /** - * Xml Serialization of a given object list. - * - * @param Type of object to be serialized. - * @param objArray Object Array to be serialized. - * @param rootName Root name for the xml. - * @param nodeName Node name for the array nodes. - * @param cls Class of object to be serialized. - * @return The serialized Xml String representation of the given object array. - * @throws IOException Signals that an IO exception occurred. - */ - public static String serializeXmlArray(T[] objArray, String rootName, String nodeName, Class cls) - throws IOException { - try { - JAXBContext context = JAXBContext.newInstance(cls); - JAXBElement jaxbElement; - String xmlBlock = "<" + rootName + ">\n"; - for (T element : objArray) { - jaxbElement = new JAXBElement<>(new QName(nodeName), cls, element); - StringWriter writer = new StringWriter(); - Marshaller marshaller = context.createMarshaller(); - marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); - marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true); - marshaller.marshal(jaxbElement, writer); - xmlBlock += " " + writer.toString() + "\n"; - } - - xmlBlock += ""; - return xmlBlock; - } catch (JAXBException jaxbException) { - throw new IOException(jaxbException); - } - } - - /** - * Xml Serialization of a given object. - * - * @param Type of object to be serialized. - * @param obj Object to be serialized. - * @param rootName Root name for the xml. - * @param cls Class of object to be serialized. - * @return The serialized Xml String representation of the given object. - * @throws IOException Signals that an IOException exception occurred. - */ - public static String serializeXml(T obj, String rootName, Class cls) throws IOException { - try { - JAXBContext context = JAXBContext.newInstance(obj.getClass()); - JAXBElement elem = new JAXBElement<>(new QName(rootName), cls, obj); - - StringWriter writer = new StringWriter(); - Marshaller marshaller = context.createMarshaller(); - marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); - marshaller.marshal(elem, writer); - return writer.toString(); - } catch (JAXBException jaxbException) { - throw new IOException(jaxbException); - } - } - - /** - * Json Serialization of a given container object based on annotation. - * - * @param obj The object to serialize into Json. - * @return The serialized Json String representation of the given object. - * @throws JsonProcessingException Signals that a Json Processing Exception has - * occurred. - */ - public static String serializeTypeCombinator(Object obj) throws JsonProcessingException { - if (obj == null) { - return null; - } - - Annotation stringCaseAnnotation = obj.getClass().getAnnotation(TypeCombinatorStringCase.class); - - if (stringCaseAnnotation != null) { - return obj.toString(); - } - - return serialize(obj); - } - - /** - * Json deserialization of the given Json string using a specified - * JsonDerializer. - * - * @param jsonNode The JsonNode to deserialize. - * @param typeReference TypeReference of T1. - * @param The type of the object to deserialize into. - * @param The type of the custom deserializer. - * @param cls The class to attach the deserializer to. - * @param deserializer The deserializer to use. - * @return The deserialized object. - * @throws IOException Signals if any I/O exception occurred. - */ - public static T1 deserialize(JsonNode jsonNode, - final TypeReference typeReference, final Class cls, final JsonDeserializer deserializer) - throws IOException { - if (jsonNode == null) { - return null; - } - - return deserialize(mapper.writeValueAsString(jsonNode), typeReference, cls, deserializer); - } - - /** - * Json deserialization of the given Json string using a specified - * JsonDerializer. - * - * @param json The Json string to deserialize. - * @param typeReference TypeReference of T1. - * @param The type of the object to deserialize into. - * @param The type of the custom deserializer. - * @param cls The class to attach the deserializer to. - * @param deserializer The deserializer to use. - * @return The deserialized object. - * @throws IOException Signals if any I/O exception occurred. - */ - public static T1 deserialize(String json, - final TypeReference typeReference, final Class cls, final JsonDeserializer deserializer) - throws IOException { - if (isNullOrWhiteSpace(json)) { - return null; - } - - return new ObjectMapper() { - private static final long serialVersionUID = -1639089569991988232L; - { - SimpleModule module = new SimpleModule(); - module.addDeserializer(cls, deserializer); - this.registerModule(module); - } - }.readValue(json, typeReference); - } - - /** - * Json deserialization of the given Json string. - * - * @param The type of the object to deserialize into. - * @param json The Json string to deserialize. - * @param clazz The type of the object to deserialize into. - * @return The deserialized object. - * @throws IOException Signals if any I/O exception occurred. - */ - public static T deserialize(String json, Class clazz) throws IOException { - if (isNullOrWhiteSpace(json)) { - return null; - } - - return mapper.readValue(json, clazz); - } - - /** - * Strict JSON deserialization of the given JSON string with - * FAIL_ON_UNKNOWN_PROPERTIES flag as true, used particularly for type - * combinators. - * - * @param The type of the object to deserialize into - * @param json The JsonNode to deserialize - * @param classes The list of types of the object to deserialize into - * @param isOneOf The boolean flag to validate for oneOf flow - * @return The deserialized object - * @throws IOException Signals if any I/O exception occurred. - */ - public static T deserialize(JsonNode json, List> classes, boolean isOneOf) - throws IOException { - if (json == null) { - return null; - } - - T deserializedObject = null; - String mappedType = null; - List unMappedTypes = new ArrayList(); - boolean matchFound = false; - for (Class clazz : classes) { - String classType = getTypeCombinatorCaseType(clazz); - try { - deserializedObject = strictMapper.convertValue(json, clazz); - if (isOneOf && matchFound) { - throw new OneOfValidationException(mappedType, classType, json); - } - mappedType = classType; - matchFound = true; - if (!isOneOf) { - break; - } - } catch (IllegalArgumentException e) { - unMappedTypes.add(classType); - } - } - if (matchFound) { - return deserializedObject; - } - if (isOneOf) { - throw new OneOfValidationException(unMappedTypes, json); - } - throw new AnyOfValidationException(unMappedTypes, json); - } - - private static String getTypeCombinatorCaseType(Class clazz) { - TypeCombinatorCase caseAnnotation = clazz.getAnnotation(TypeCombinatorCase.class); - if (caseAnnotation == null) { - return ""; - } - return caseAnnotation.type(); - } - - /** - * Json deserialization of the given Json string. - * - * @param json The Json string to deserialize. - * @return The deserialized Json as a Map. - * @throws IOException Signals if any I/O exception occurred. - */ - public static LinkedHashMap deserialize(String json) throws IOException { - if (isNullOrWhiteSpace(json)) { - return null; - } - - TypeReference> typeRef = new TypeReference>() { - }; - return deserialize(json, typeRef); - } - - /** - * JSON Deserialization of the given json string. - * - * @param json The json string to deserialize. - * @param typeReference TypeReference of T. - * @param The type of the object to deserialize into. - * @return The deserialized object. - * @throws IOException Signals if any I/O exception occurred. - */ - public static T deserialize(String json, TypeReference typeReference) throws IOException { - if (isNullOrWhiteSpace(json)) { - return null; - } - - return mapper.readValue(json, typeReference); - } - - /** - * JSON Deserialization of the given json string with FAIL_ON_UNKNOWN_PROPERTIES - * flag as true. - * - * @param jsonNode The Json Node to deserialize. - * @param typeReference TypeReference of T. - * @param The type of the object to deserialize into. - * @return The deserialized object. - * @throws IOException Signals if any I/O exception occurred. - */ - public static T deserialize(JsonNode jsonNode, TypeReference typeReference) - throws IOException { - if (jsonNode == null) { - return null; - } - - return strictMapper.convertValue(jsonNode, typeReference); - } - - /** - * JSON deserialization of the given JsonNode with FAIL_ON_UNKNOWN_PROPERTIES - * flag as true. - * - * @param The type of the object to deserialize into. - * @param jsonNode The Json Node to deserialize. - * @param clazz The type of the object to deserialize into. - * @return The deserialized object. - * @throws IOException Signals if any I/O exception occurred. - */ - public static T deserialize(JsonNode jsonNode, Class clazz) throws IOException { - if (jsonNode == null) { - return null; - } - return strictMapper.convertValue(jsonNode, clazz); - } - - /** - * XML Deserialization of the given xml string. - * - * @param The class of the object to deserialize into. - * @param xml The xml string to deserialize. - * @param cls The class of the object to deserialize into. - * @return The deserialized object. - * @throws IOException Signals if any I/O exception occurred. - */ - public static T deserializeXml(String xml, Class cls) throws IOException { - try { - JAXBContext jaxbContext = JAXBContext.newInstance(cls); - Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); - StringReader reader = new StringReader(xml); - JAXBElement jaxbElement = jaxbUnmarshaller.unmarshal(new StreamSource(reader), cls); - - return jaxbElement.getValue(); - } catch (JAXBException jaxbException) { - throw new IOException(jaxbException); - } - } - - /** - * XML Deserialization of the given xml string. - * - * @param The class of the object to deserialize into. - * @param xml The xml string to deserialize. - * @param cls The class of the object to deserialize into. - * @return The deserialized object list. - * @throws IOException Signals if any I/O exception occurred. - */ - public static List deserializeXmlArray(String xml, Class cls) throws IOException { - try { - JAXBContext jaxbContext = JAXBContext.newInstance(cls); - Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); - StringReader reader = new StringReader(xml); - JAXBElement jaxbElement = jaxbUnmarshaller.unmarshal(new StreamSource(reader), cls); - - return Arrays.asList(jaxbElement.getValue()); - } catch (JAXBException jaxbException) { - throw new IOException(jaxbException); - } - } - - /** - * XML Deserialization of the given xml string for simple types. - * - * @param The class of the object to deserialize into. - * @param xml The xml string to deserialize. - * @param cls The class of the object to deserialize into. - * @return The deserialized simple types object list. - * @throws IOException Signals if any I/O exception occurred. - */ - public static List deserializeXmlSimpleTypesArray(String xml, Class cls) - throws IOException { - try { - JAXBContext jaxbContext = JAXBContext.newInstance(cls); - Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); - List deserializedList = new ArrayList<>(); - Pattern pattern = Pattern.compile("<.+?>(.+?)"); - Matcher patternMatcher = pattern.matcher(xml); - while (patternMatcher.find()) { - StringReader reader = new StringReader(patternMatcher.group()); - T unmarshalledElement = jaxbUnmarshaller.unmarshal(new StreamSource(reader), cls).getValue(); - deserializedList.add(unmarshalledElement); - } - return deserializedList; - } catch (JAXBException jaxbException) { - throw new IOException(jaxbException); - } - } - - /** - * JSON Deserialization from custom deserializer based on given discriminator - * and registry. - * - * @param jp Parsed used for reading JSON content. - * @param ctxt Context that can be used to access - * information about this deserialization - * activity. - * @param discriminator The model's discriminator. - * @param registry The map containing all discriminators as - * keys and associated classes as values. - * @param typesWithoutDiscriminator The list containing all types without - * discriminators. - * @param isOneOf The boolean flag to validate for oneOf flow. - * @param the deserialized response type. - * @return The deserialized object. - * @throws IOException Signals if any I/O exception occurred. - */ - public static T deserialize(JsonParser jp, DeserializationContext ctxt, String discriminator, - List>> registry, List> typesWithoutDiscriminator, - boolean isOneOf) throws IOException { - ObjectCodec oc = jp.getCodec(); - JsonNode jsonNode = oc.readTree(jp); - List> types = deduceType(jsonNode, discriminator, registry); - - if (types == null || types.isEmpty()) { - if (typesWithoutDiscriminator != null && !typesWithoutDiscriminator.isEmpty()) { - types = typesWithoutDiscriminator; - } else { - throw new IOException("Discriminator is missing."); - } - } - - return deserialize(jsonNode, types, isOneOf); - } - - /** - * Json deserialization of the given Json string. - * - * @param json The Json string to deserialize. - * @return The deserialized Json as an Object. - */ - public static Object deserializeAsObject(String json) { - if (isNullOrWhiteSpace(json)) { - return null; - } - try { - return CoreHelper.deserialize(json, new TypeReference() { - }); - } catch (IOException e) { - // Failed to deserialize when json is not representing a JSON object. - // i.e. either its string or any primitive type. - return json; - } - } - - /** - * JSON Deserialization of the given json string. - * - * @param The type of the object to deserialize into. - * @param json The Json string to deserialize. - * @param classArray The class of the array of objects to deserialize into. - * @return The deserialized list of objects. - * @throws IOException Signals if any I/O exception occurred.. - */ - public static List deserializeArray(String json, Class classArray) throws IOException { - if (isNullOrWhiteSpace(json)) { - return null; - } - - return Arrays.asList(mapper.readValue(json, classArray)); - } - - /** - * Replaces template parameters in the given URL. - * - * @param queryBuilder The query string builder to replace the template - * parameters. - * @param parameters The parameters to replace in the URL. - */ - public static void appendUrlWithTemplateParameters(StringBuilder queryBuilder, - Map> parameters) { - // Perform parameter validation - if (null == queryBuilder) { - throw new IllegalArgumentException("Given value for parameter \"queryBuilder\" is invalid."); - } - - if (null == parameters) { - return; - } - - // Iterate and append parameters - for (Map.Entry> pair : parameters.entrySet()) { - - String replaceValue = ""; - Object element = pair.getValue().getKey(); - boolean shouldEncode = pair.getValue().getValue(); - - // Load element value as string - if (null == element) { - replaceValue = ""; - } else if (element instanceof Collection) { - replaceValue = flattenCollection("", (Collection) element, shouldEncode, "%s%s%s", '/'); - } else { - if (shouldEncode) { - replaceValue = tryUrlEncode(element.toString(), false); - } else { - replaceValue = element.toString(); - } - } - - // Find the template parameter and replace it with its value - replaceAll(queryBuilder, "{" + pair.getKey() + "}", replaceValue); - } - } - - /** - * Appends the given set of parameters to the given query string. - * - * @param queryBuilder The query URL string to append the - * parameters. - * @param parameters The parameters to append. - * @param arraySerializationFormat the array serialization format. - */ - public static void appendUrlWithQueryParameters(StringBuilder queryBuilder, Map parameters, - ArraySerializationFormat arraySerializationFormat) { - // Perform parameter validation - if (queryBuilder == null) { - throw new IllegalArgumentException("Given value for parameter \"queryBuilder\" is invalid."); - } - if (parameters == null || parameters.isEmpty()) { - return; - } - - // Check if query string already has parameters - boolean hasParams = queryBuilder.indexOf("?") > 0; - queryBuilder.append(hasParams ? '&' : '?'); - - encodeObjectAsQueryString("", parameters, queryBuilder, arraySerializationFormat); - } - - /** - * Validates if the string is null, empty or whitespace. - * - * @param s The string to validate. - * @return The result of validation. - */ - public static boolean isNullOrWhiteSpace(String s) { - if (s == null) { - return true; - } - - int length = s.length(); - if (length > 0) { - for (int start = 0, middle = length / 2, end = length - 1; start <= middle; start++, end--) { - if (s.charAt(start) > ' ' || s.charAt(end) > ' ') { - return false; - } - } - return true; - } - return false; - } - - /** - * Replaces all occurrences of the given string in the string builder. - * - * @param stringBuilder The string builder to update with replaced strings. - * @param toReplace The string to replace in the string builder. - * @param replaceWith The string to replace with. - */ - public static void replaceAll(StringBuilder stringBuilder, String toReplace, String replaceWith) { - int index = stringBuilder.indexOf(toReplace); - - while (index != -1) { - stringBuilder.replace(index, index + toReplace.length(), replaceWith); - index += replaceWith.length(); // Move to the end of the replacement - index = stringBuilder.indexOf(toReplace, index); - } - } - - /** - * Updates the user agent header value. - * - * @param apiUserAgent the String value of apiUserAgent. - * @param userAgentConfig the Map of user agent config. - * @return {@link String}. - */ - public static String updateUserAgent(String apiUserAgent, Map userAgentConfig) { - String engineVersion = System.getProperty("java.runtime.version"); - String osName = System.getProperty("os.name") + "-" + System.getProperty("os.version"); - userAgent = apiUserAgent; - userAgent = userAgent.replace("{engine}", "JRE"); - userAgent = userAgent.replace("{engine-version}", engineVersion != null ? engineVersion : ""); - userAgent = userAgent.replace("{os-info}", osName != null ? osName : ""); - - if (userAgentConfig != null) { - userAgentConfig.forEach((key, value) -> { - userAgent = userAgent.replace(key, value); - }); - } - - return userAgent; - } - - /** - * Removes null values from the given map. - * - * @param map Map of values. - */ - public static void removeNullValues(Map map) { - if (map == null) { - return; - } - - map.values().removeAll(Collections.singleton(null)); - } - - /** - * Validates and processes the given URL. - * - * @param url The given URL to process. - * @return Pre-process URL as string. - */ - public static String cleanUrl(StringBuilder url) { - // Ensures that the URLs are absolute - Pattern pattern = Pattern.compile("^(https?://[^/]+)"); - Matcher matcher = pattern.matcher(url); - if (!matcher.find()) { - throw new IllegalArgumentException("Invalid Url format."); - } - - // Get the http protocol match - String protocol = matcher.group(1); - - // Removes redundant forward slashes - String query = url.substring(protocol.length()); - query = query.replaceAll("//+", "/"); - - // Returns processed URL - return protocol.concat(query); - } - - /** - * Prepares Array style form fields from a given array of values. - * - * @param value Value for the form fields. - * @param arraySerializationFormat serialization format. - * @return Dictionary of form fields created from array elements. - */ - public static List> prepareFormFields(Map value, - ArraySerializationFormat arraySerializationFormat) { - List> formFields = new ArrayList<>(); - if (value != null) { - objectToList("", value, formFields, new HashSet(), arraySerializationFormat); - } - return formFields; - } - - /** - * JSON Deserialization of the given json string with FAIL_ON_UNKNOWN_PROPERTIES - * flag as true. - * - * @param The type of the object to deserialize into. - * @param json The Json string to deserialize. - * @param classArray The class of the array of objects to deserialize into. - * @return The deserialized list of objects. - * @throws IOException Signals if any I/O exception occurred. - */ - public static List deserializeArray(JsonNode json, Class classArray) throws IOException { - if (json == null) { - return null; - } - - return Arrays.asList(strictMapper.convertValue(json, classArray)); - } - - /** - * Deduces the type based on given discriminator and registry. - * - * @param jsonNode The json to check against. - * @param discriminator The model's discriminator. - * @param registry The Map containing all discriminators as keys and - * associated classes as values. - * @param The type of the object to deserialize into. - * @return The type to deserialize into. - * @throws IOException Signals if any I/O exception occurred. - */ - private static List> deduceType(JsonNode jsonNode, String discriminator, - List>> registry) throws IOException { - if (jsonNode == null || registry == null) { - return null; - } - - final String discriminatorValue; - if (jsonNode.isArray()) { - if (jsonNode.has(0) && jsonNode.get(0).has(discriminator)) { - // JSON is an array of model objects - discriminatorValue = jsonNode.get(0).get(discriminator).asText(); - } else { - discriminatorValue = deduceTypeFromImmidiateChild(jsonNode.get(0), discriminator); - } - } else { - if (jsonNode.has(discriminator)) { - // JSON is a model object - discriminatorValue = jsonNode.get(discriminator).asText(); - } else { - // JSON is a Map so deduce discriminator for first child only - discriminatorValue = deduceTypeFromImmidiateChild(jsonNode, discriminator); - } - } - - return registry.stream().filter(item -> item.get(discriminatorValue) != null) - .map(item -> item.get(discriminatorValue)).distinct().collect(Collectors.toList()); - } - - /** - * Deduces the type from immediate child if exists based on given discriminator. - * - * @param jsonNode The json to check against. - * @param discriminator The model's discriminator. - * @return The type to deserialize into. - * @throws IOException Signals if any I/O exception occurred. - */ - private static String deduceTypeFromImmidiateChild(JsonNode jsonNode, String discriminator) { - Iterator iterator = jsonNode.iterator(); - while (iterator.hasNext()) { - JsonNode tempNode = iterator.next(); - if (tempNode.isArray()) { - if (tempNode.has(0) && tempNode.get(0).has(discriminator)) { - // JSON is an array of model objects - return tempNode.get(0).get(discriminator).asText(); - } - } else { - if (tempNode.has(discriminator)) { - // JSON is a model object - return tempNode.get(discriminator).asText(); - } - } - } - return null; - } - - /** - * Encodes a given object to URL encoded string. - * - * @param name Name of the object. - * @param obj Raw object sent from caller. - * @param objBuilder String of elements. - * @param arraySerializationFormat The array serialization format. - */ - private static void encodeObjectAsQueryString(String name, Object obj, StringBuilder objBuilder, - ArraySerializationFormat arraySerializationFormat) { - - List> objectList = new ArrayList<>(); - objectToList(name, obj, objectList, new HashSet(), arraySerializationFormat); - boolean hasParam = false; - List arrays = new ArrayList(); - - for (SimpleEntry pair : objectList) { - String accessor = pair.getKey(); - // Ignore null - Object value = pair.getValue(); - if (value == null) { - continue; - } - - hasParam = true; - // Load element value as string - - if (accessor.matches(".*?\\[\\d+\\]$") && isDelimeterFormat(arraySerializationFormat)) { - - String arrayName = accessor.substring(0, accessor.lastIndexOf('[')); - - if (arrays.contains(arrayName)) { - objBuilder.setLength(objBuilder.length() - 1); - accessor = getAccessorStringFormat(arraySerializationFormat); - } else { - accessor = arrayName + "="; - } - - if (!arrays.contains(arrayName)) { - arrays.add(arrayName); - } - - appendParamKeyValuePair("%s%s&", objBuilder, accessor, value); - } else { - appendParamKeyValuePair("%s=%s&", objBuilder, accessor, value); - } - } - - // Removing the last & - if (hasParam) { - objBuilder.setLength(objBuilder.length() - 1); - } - } - - private static void appendParamKeyValuePair(String formatString, StringBuilder objBuilder, String accessor, - Object value) { - - String paramKeyValPair = String.format(formatString, accessor, tryUrlEncode(value.toString(), false)); - objBuilder.append(paramKeyValPair); - } - - /** - * Flattening a collection of objects into a string. - * - * @param elemName The element name of collection. - * @param array Array of elements to flatten. - * @param encode Need to encode?. - * @param fmt Format string to use for array flattening. - * @param separator Separator to use for string concatenation. - * @return Representative string made up of array elements. - */ - private static String flattenCollection(String elemName, Collection array, boolean encode, String fmt, - char separator) { - StringBuilder builder = new StringBuilder(); - - // Append all elements of the array into a string - for (Object element : array) { - String elemValue = null; - - // Replace null values with empty string to maintain index order - if (element == null) { - elemValue = ""; - } else { - elemValue = element.toString(); - } - if (encode) { - elemValue = tryUrlEncode(elemValue, false); - } - builder.append(String.format(fmt, elemName, elemValue, separator)); - } - - // Remove the last separator, if appended - if ((builder.length() > 1) && (builder.charAt(builder.length() - 1) == separator)) { - builder.deleteCharAt(builder.length() - 1); - } - - return builder.toString(); - } - - /** - * Tries URL encode using UTF-8. - * - * @param value The value to URL encode. - * @param spaceAsPercentEncoded The flag get space character as percent encoded. - * @return Encoded url. - */ - public static String tryUrlEncode(String value, boolean spaceAsPercentEncoded) { - try { - String encodedUrl = URLEncoder.encode(value, "UTF-8"); - if (spaceAsPercentEncoded) { - return encodedUrl.replace("+", "%20"); - } - return encodedUrl; - } catch (UnsupportedEncodingException ex) { - return value; - } - } - - /** - * Responsible to encode into base64 the username and password - * - * @param basicAuthUserName The auth username - * @param basicAuthPassword The auth password - * @return The base64 encoded String - */ - public static String getBase64EncodedCredentials(String basicAuthUserName, String basicAuthPassword) { - String authCredentials = basicAuthUserName + ":" + basicAuthPassword; - return "Basic " + Base64.getEncoder().encodeToString(authCredentials.getBytes()); - } - - private static void objectToList(String objName, Collection obj, List> objectList, - HashSet processed, ArraySerializationFormat arraySerializationFormat) { - - Collection array = obj; - // Append all elements of the array into a string - int index = 0; - for (Object element : array) { - // load key value pair - String key; - - if (isWrapperType(element) && (arraySerializationFormat == ArraySerializationFormat.UNINDEXED - || arraySerializationFormat == ArraySerializationFormat.PLAIN)) { - key = arraySerializationFormat == ArraySerializationFormat.UNINDEXED ? String.format("%s[]", objName) - : objName; - } else { - key = String.format("%s[%d]", objName, index++); - } - loadKeyValuePairForEncoding(key, element, objectList, processed, arraySerializationFormat); - } - - } - - private static void objectToList(String objName, Map obj, List> objectList, - HashSet processed, ArraySerializationFormat arraySerializationFormat) { - // Process map - Map map = obj; - // Append all elements of the array into a string - for (Map.Entry pair : map.entrySet()) { - String attribName = pair.getKey().toString(); - if ((objName != null) && (!objName.isEmpty())) { - attribName = String.format("%s[%s]", objName, attribName); - } - loadKeyValuePairForEncoding(attribName, pair.getValue(), objectList, processed, arraySerializationFormat); - } - } - - /** - * Converts a given object to a form encoded map. - * - * @param objName Name of the object. - * @param obj The object to convert into a map. - * @param objectList The object list to populate. - * @param processed List of object hashCodes that are already - * parsed. - * @param arraySerializationFormat The array serialization format. - */ - private static void objectToList(String objName, Object obj, List> objectList, - HashSet processed, ArraySerializationFormat arraySerializationFormat) { - // Null values need not to be processed - if (obj == null) { - return; - } - - // Wrapper types are autoboxed, so reference checking is not needed - Class clazz = obj.getClass(); - - Annotation typeCombinatorAnnotation = clazz.getAnnotation(TypeCombinatorCase.class); - if (!isWrapperType(clazz) && typeCombinatorAnnotation == null) { - // Avoid infinite recursion - if (processed.contains(objName.hashCode())) { - return; - } - processed.add(objName.hashCode()); - } - - // Process arrays - if (obj instanceof Collection) { - objectToList(objName, (Collection) obj, objectList, processed, arraySerializationFormat); - } else if (obj.getClass().isArray()) { - // Process array - - Object[] array = (Object[]) obj; - // Append all elements in the array into a string - int index = 0; - for (Object element : array) { - // Load key value pair - String key = String.format("%s[%d]", objName, index++); - loadKeyValuePairForEncoding(key, element, objectList, processed, arraySerializationFormat); - } - } else if (obj instanceof Map) { - objectToList(objName, (Map) obj, objectList, processed, arraySerializationFormat); - } else { - // Process objects - if (typeCombinatorAnnotation != null) { - for (Field field : clazz.getDeclaredFields()) { - // unexpected field $jococoData came for unit test coverage and makes test fails - if (field.getName() == "$jacocoData") { - continue; - } - - Object fieldValue = null; - Annotation serializeAnnotation = null; - try { - field.setAccessible(true); - fieldValue = field.get(obj); - serializeAnnotation = field.getAnnotation(JsonSerialize.class); - if (serializeAnnotation == null) { - serializeAnnotation = field.getAnnotation(FormSerialize.class); - } - } catch (IllegalArgumentException | IllegalAccessException e) { - // Ignoring the exception - } - - if (serializeAnnotation != null) { - if (serializeAnnotation instanceof JsonSerialize) { - loadKeyValuePairForEncoding(objName, fieldValue, objectList, processed, - (JsonSerialize) serializeAnnotation, arraySerializationFormat); - } else { - loadKeyValuePairForEncoding(objName, fieldValue, objectList, processed, - (FormSerialize) serializeAnnotation, arraySerializationFormat); - } - } else { - loadKeyValuePairForEncoding(objName, fieldValue, objectList, processed, - arraySerializationFormat); - } - } - return; - } - // Invoke getter methods - while (clazz != null) { - for (Method method : clazz.getDeclaredMethods()) { - - // Is a public/protected getter or internalGetter? - if (method.getParameterTypes().length != 0 || Modifier.isPrivate(method.getModifiers()) - || (!method.getName().startsWith("get") && !method.getName().startsWith("internalGet"))) { - continue; - } - - // Get JsonGetter annotation - Annotation getterAnnotation = method.getAnnotation(JsonGetter.class); - if (getterAnnotation == null) { - continue; - } - - // Load key name from getter attribute name - String attribName = ((JsonGetter) getterAnnotation).value(); - if ((objName != null) && (!objName.isEmpty())) { - attribName = String.format("%s[%s]", objName, attribName); - } - - try { - // Load value by invoking getter method - method.setAccessible(true); - Object value = method.invoke(obj); - JsonSerialize serializerAnnotation = method.getAnnotation(JsonSerialize.class); - // Load key value pair into objectList - if (serializerAnnotation != null) { - loadKeyValuePairForEncoding(attribName, value, objectList, processed, serializerAnnotation, - arraySerializationFormat); - } else { - loadKeyValuePairForEncoding(attribName, value, objectList, processed, - arraySerializationFormat); - } - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - // This block only calls getter methods. - // These getters don't throw any exception except invocationTargetException. - // The getters are public so there is no chance of an IllegalAccessException - // Steps we've followed ensure that the object has the specified method. - } - } - clazz = clazz.getSuperclass(); - } - } - } - - private static String getAccessorStringFormat(ArraySerializationFormat arraySerializationFormat) { - switch (arraySerializationFormat) { - case CSV: - return CSV_FORMAT; - case PSV: - return PSV_FORMAT; - case TSV: - return TSV_FORMAT; - default: - return ""; - } - } - - private static boolean isDelimeterFormat(ArraySerializationFormat arraySerializationFormat) { - return (arraySerializationFormat == ArraySerializationFormat.CSV - || arraySerializationFormat == ArraySerializationFormat.TSV - || arraySerializationFormat == ArraySerializationFormat.PSV); - } - - /** - * Processes the value and load into objectList against key. - * - * @param key The key to used for creation of key value - * pair. - * @param value The value to process against the given key. - * @param objectList The object list to process with key value - * pair. - * @param processed List of processed objects hashCodes. - * @param serializer The serializer for serialize the object. - * @param arraySerializationFormat The array serialization format. - * @throws JsonProcessingException Signals that a Json Processing Exception has - * occurred. - */ - private static void loadKeyValueUsingSerializer(String key, Object value, - List> objectList, HashSet processed, JsonSerializer serializer, - ArraySerializationFormat arraySerializationFormat) throws JsonProcessingException { - value = serialize(value, serializer); - - Object obj = deserializeAsObject(value.toString()); - if (obj instanceof List || obj instanceof Map) { - loadKeyValuePairForEncoding(key, obj, objectList, processed, arraySerializationFormat); - } else { - if (value.toString().startsWith("\"")) { - value = value.toString().substring(1, value.toString().length() - 1); - } - objectList.add(new SimpleEntry(key, value)); - } - } - - /** - * While processing objects to map, loads value after serializing. - * - * @param key The key to used for creation of key value - * pair. - * @param value The value to process against the given key. - * @param objectList The object list to process with key value - * pair. - * @param processed List of processed objects hashCodes. - * @param formSerializerAnnotation Annotation for serializer. - * @param arraySerializationFormat The array serialization format. - */ - private static void loadKeyValuePairForEncoding(String key, Object value, - List> objectList, HashSet processed, - FormSerialize formSerializerAnnotation, ArraySerializationFormat arraySerializationFormat) { - if (value == null) { - return; - } - - try { - JsonSerializer serializer = getCollectionCustomSerializer(formSerializerAnnotation); - loadKeyValueUsingSerializer(key, value, objectList, processed, serializer, arraySerializationFormat); - } catch (JsonProcessingException e) { - e.printStackTrace(); - } - } - - /** - * While processing objects to map, decides whether to perform recursion or load - * value. - * - * @param key The key for creating key value pair. - * @param value The value to process against the given key. - * @param objectList The object list to process with key value - * pair. - * @param processed List of processed objects hashCodes. - * @param arraySerializationFormat The array serialization format. - */ - private static void loadKeyValuePairForEncoding(String key, Object value, - List> objectList, HashSet processed, - ArraySerializationFormat arraySerializationFormat) { - if (value == null) { - return; - } - if (isWrapperType(value)) { - objectList.add(new SimpleEntry(key, value)); - } else if (value instanceof CoreJsonObject) { - objectToList(key, ((CoreJsonObject) value).getStoredObject(), objectList, processed, - arraySerializationFormat); - } else if (value instanceof CoreJsonValue) { - Object storedValue = ((CoreJsonValue) value).getStoredObject(); - if (isWrapperType(storedValue)) { - objectList.add(new SimpleEntry(key, storedValue)); - } else { - objectToList(key, storedValue, objectList, processed, arraySerializationFormat); - } - } else if (value instanceof UUID) { - // UUIDs can be converted to string - objectList.add(new SimpleEntry(key, value.toString())); - } else { - objectToList(key, value, objectList, processed, arraySerializationFormat); - } - } - - /** - * While processing objects to map, loads value after serializing. - * - * @param key The key to used for creation of key value - * pair. - * @param value The value to process against the given key. - * @param objectList The object list to process with key value - * pair. - * @param processed List of processed objects hashCodes. - * @param serializerAnnotation Annotation for serializer. - * @param arraySerializationFormat The array serialization format. - */ - private static void loadKeyValuePairForEncoding(String key, Object value, - List> objectList, HashSet processed, - JsonSerialize serializerAnnotation, ArraySerializationFormat arraySerializationFormat) { - if (value == null) { - return; - } - - try { - JsonSerializer serializer = getSerializer(serializerAnnotation); - if (serializer == null) { - serializer = getCollectionSerializer(serializerAnnotation); - } - - loadKeyValueUsingSerializer(key, value, objectList, processed, serializer, arraySerializationFormat); - } catch (JsonProcessingException e) { - e.printStackTrace(); - } - } - - /** - * Check if the given object can be wrapped directly. - * - * @param object The given object. - * @return true if the class is an autoboxed class e.g., Integer. - */ - private static boolean isWrapperType(Object object) { - if (object == null) { - return false; - } - return WRAPPER_TYPES.contains(object.getClass()) || object.getClass().isPrimitive() - || object.getClass().isEnum(); - } - - /** - * Json Serialization of an ENUM defined under oneOf/anyOf container. - * - * @param value The object to serialize into Json String. - * @return The serialized Json String representation of the given object. - * @throws JsonProcessingException Signals that a Json Processing Exception has - * occurred. - */ - public static String serializeEnumContainer(Object value) throws JsonProcessingException { - if (value instanceof String || value instanceof Integer) { - return String.valueOf(value); - } - - return serialize(value); - } - - /** - * Custom deserializer class of any string property for disallowing implicit - * type conversion. - */ - private static class CoercionLessStringDeserializer extends StringDeserializer { - private static final long serialVersionUID = 1L; - - @Override - public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - - if (p.getCurrentToken() != JsonToken.VALUE_STRING) { - String message = "Cannot coerce " + p.getCurrentToken() + " to String value"; - throw MismatchedInputException.from(p, String.class, message); - } - return super.deserialize(p, ctxt); - } - } + /** + * A string of user agent. + */ + private static String userAgent; + + /** + * A tab separated array serialization format. + */ + private static final String TSV_FORMAT = "%09"; + + /** + * A comma separated array serialization format. + */ + private static final String CSV_FORMAT = ","; + + /** + * A pipe separated array serialization format + */ + private static final String PSV_FORMAT = "%7C"; + + /** + * Deserialization of Json data. + */ + private static ObjectMapper mapper = JsonMapper + .builder().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, + false) + .withConfigOverride(BigDecimal.class, mutableConfigOverride -> mutableConfigOverride + .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING))) + .build(); + + /** + * Strict Deserialization of Json data. + */ + private static ObjectMapper strictMapper = JsonMapper.builder() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .configure(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES, true) + .configure(MapperFeature.ALLOW_COERCION_OF_SCALARS, false) + .addModule(new SimpleModule().addDeserializer(String.class, + new CoercionLessStringDeserializer())) + .withConfigOverride(BigDecimal.class, mutableConfigOverride -> mutableConfigOverride + .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING))) + .build(); + + protected CoreHelper() { + } + + /** + * Get a JsonSerializer instance for a collection from the provided annotation. + * + * @param serializerAnnotation The Annotation containing information about the + * custom serializer of a collection. + * @return The JsonSerializer instance of the required type. + */ + private static JsonSerializer getCollectionCustomSerializer( + FormSerialize serializerAnnotation) { + try { + return serializerAnnotation.contentUsing().getDeclaredConstructor().newInstance(); + } catch (Exception e) { + return null; + } + } + + /** + * List of classes that are wrapped directly. This information is needed when + * traversing object trees for reference matching. + */ + private static final Set WRAPPER_TYPES = new HashSet( + Arrays.asList(Boolean.class, Character.class, Byte.class, Short.class, String.class, + Integer.class, Long.class, Float.class, Double.class, BigDecimal.class, + Void.class, File.class, MultipartWrapper.class, MultipartFileWrapper.class)); + + /** + * Get a JsonSerializer instance from the provided annotation. + * + * @param serializerAnnotation The Annotation containing information about the + * serializer. + * @return The JsonSerializer instance of the required type. + */ + private static JsonSerializer getSerializer(JsonSerialize serializerAnnotation) { + try { + return serializerAnnotation.using().getDeclaredConstructor().newInstance(); + } catch (Exception e) { + return null; + } + } + + /** + * Get a JsonSerializer instance for a collection from the provided annotation. + * + * @param serializerAnnotation The Annotation containing information about the + * serializer of a collection. + * @return The JsonSerializer instance of the required type. + */ + private static JsonSerializer getCollectionSerializer(JsonSerialize serializerAnnotation) { + try { + return serializerAnnotation.contentUsing().getDeclaredConstructor().newInstance(); + } catch (Exception e) { + return null; + } + } + + /** + * Deserialization of Json data. + * + * @return {@link ObjectMapper}. + */ + public static ObjectMapper getMapper() { + return mapper; + } + + /** + * Strict Deserialization of Json data. + * + * @return {@link ObjectMapper}. + */ + public static ObjectMapper getStrictMapper() { + return strictMapper; + } + + /** + * Json Serialization of a given object. + * + * @param obj The object to serialize into Json. + * @return The serialized Json String representation of the given object. + * @throws JsonProcessingException Signals that a Json Processing Exception has + * occurred. + */ + public static String serialize(Object obj) throws JsonProcessingException { + if (obj == null) { + return null; + } + + return mapper.writeValueAsString(obj); + } + + /** + * Json Serialization of a given object using a specified JsonSerializer. + * + * @param obj The object to serialize into Json. + * @param serializer The instance of JsonSerializer to use. + * @return The serialized Json string representation of the given object. + * @throws JsonProcessingException Signals that a Json Processing Exception has + * occurred. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static String serialize(Object obj, final JsonSerializer serializer) + throws JsonProcessingException { + if (obj == null || serializer == null) { + return null; + } + + Class cls = null; + if (obj.getClass().getName().equals("java.util.ArrayList")) { + // need to find the generic type if it's an ArrayList + cls = ((ArrayList) obj).get(0).getClass(); + } else if (obj.getClass().getName().equals("java.util.LinkedHashMap")) { + cls = ((LinkedHashMap) obj).values().toArray()[0].getClass(); + } else { + cls = obj.getClass(); + } + + ObjectMapper objectMapper = new ObjectMapper(); + SimpleModule module = new SimpleModule(); + module.addSerializer(cls, serializer); + objectMapper.registerModule(module); + + return objectMapper.writeValueAsString(obj); + } + + /** + * Xml Serialization of a given object list. + * + * @param Type of object to be serialized. + * @param objArray Object Array to be serialized. + * @param rootName Root name for the xml. + * @param nodeName Node name for the array nodes. + * @param cls Class of object to be serialized. + * @return The serialized Xml String representation of the given object array. + * @throws IOException Signals that an IO exception occurred. + */ + public static String serializeXmlArray(T[] objArray, String rootName, String nodeName, + Class cls) throws IOException { + try { + JAXBContext context = JAXBContext.newInstance(cls); + JAXBElement jaxbElement; + String xmlBlock = "<" + rootName + ">\n"; + for (T element : objArray) { + jaxbElement = new JAXBElement<>(new QName(nodeName), cls, element); + StringWriter writer = new StringWriter(); + Marshaller marshaller = context.createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true); + marshaller.marshal(jaxbElement, writer); + xmlBlock += " " + writer.toString() + "\n"; + } + + xmlBlock += ""; + return xmlBlock; + } catch (JAXBException jaxbException) { + throw new IOException(jaxbException); + } + } + + /** + * Xml Serialization of a given object. + * + * @param Type of object to be serialized. + * @param obj Object to be serialized. + * @param rootName Root name for the xml. + * @param cls Class of object to be serialized. + * @return The serialized Xml String representation of the given object. + * @throws IOException Signals that an IOException exception occurred. + */ + public static String serializeXml(T obj, String rootName, Class cls) throws IOException { + try { + JAXBContext context = JAXBContext.newInstance(obj.getClass()); + JAXBElement elem = new JAXBElement<>(new QName(rootName), cls, obj); + + StringWriter writer = new StringWriter(); + Marshaller marshaller = context.createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + marshaller.marshal(elem, writer); + return writer.toString(); + } catch (JAXBException jaxbException) { + throw new IOException(jaxbException); + } + } + + /** + * Json Serialization of a given container object based on annotation. + * + * @param obj The object to serialize into Json. + * @return The serialized Json String representation of the given object. + * @throws JsonProcessingException Signals that a Json Processing Exception has + * occurred. + */ + public static String serializeTypeCombinator(Object obj) throws JsonProcessingException { + if (obj == null) { + return null; + } + + Annotation stringCaseAnnotation = obj.getClass() + .getAnnotation(TypeCombinatorStringCase.class); + + if (stringCaseAnnotation != null) { + return obj.toString(); + } + + return serialize(obj); + } + + /** + * Json deserialization of the given Json string using a specified + * JsonDerializer. + * + * @param jsonNode The JsonNode to deserialize. + * @param typeReference TypeReference of T1. + * @param The type of the object to deserialize into. + * @param The type of the custom deserializer. + * @param cls The class to attach the deserializer to. + * @param deserializer The deserializer to use. + * @return The deserialized object. + * @throws IOException Signals if any I/O exception occurred. + */ + public static T1 deserialize(JsonNode jsonNode, + final TypeReference typeReference, final Class cls, + final JsonDeserializer deserializer) throws IOException { + if (jsonNode == null) { + return null; + } + + return deserialize(mapper.writeValueAsString(jsonNode), typeReference, cls, deserializer); + } + + /** + * Json deserialization of the given Json string using a specified + * JsonDerializer. + * + * @param json The Json string to deserialize. + * @param typeReference TypeReference of T1. + * @param The type of the object to deserialize into. + * @param The type of the custom deserializer. + * @param cls The class to attach the deserializer to. + * @param deserializer The deserializer to use. + * @return The deserialized object. + * @throws IOException Signals if any I/O exception occurred. + */ + public static T1 deserialize(String json, + final TypeReference typeReference, final Class cls, + final JsonDeserializer deserializer) throws IOException { + if (isNullOrWhiteSpace(json)) { + return null; + } + + return new ObjectMapper() { + private static final long serialVersionUID = -1639089569991988232L; + { + SimpleModule module = new SimpleModule(); + module.addDeserializer(cls, deserializer); + this.registerModule(module); + } + }.readValue(json, typeReference); + } + + /** + * Json deserialization of the given Json string. + * + * @param The type of the object to deserialize into. + * @param json The Json string to deserialize. + * @param clazz The type of the object to deserialize into. + * @return The deserialized object. + * @throws IOException Signals if any I/O exception occurred. + */ + public static T deserialize(String json, Class clazz) throws IOException { + if (isNullOrWhiteSpace(json)) { + return null; + } + + return mapper.readValue(json, clazz); + } + + /** + * Strict JSON deserialization of the given JSON string with + * FAIL_ON_UNKNOWN_PROPERTIES flag as true, used particularly for type + * combinators. + * + * @param The type of the object to deserialize into + * @param json The JsonNode to deserialize + * @param classes The list of types of the object to deserialize into + * @param isOneOf The boolean flag to validate for oneOf flow + * @return The deserialized object + * @throws IOException Signals if any I/O exception occurred. + */ + public static T deserialize(JsonNode json, List> classes, + boolean isOneOf) throws IOException { + if (json == null) { + return null; + } + + T deserializedObject = null; + String mappedType = ""; + List unMappedTypes = new ArrayList(); + boolean matchFound = false; + for (Class clazz : classes) { + String classType = getTypeCombinatorCaseType(clazz); + try { + deserializedObject = strictMapper.convertValue(json, clazz); + if (isOneOf && matchFound) { + throw new OneOfValidationException(mappedType, classType, json); + } + mappedType = classType; + matchFound = true; + if (!isOneOf) { + break; + } + } catch (IllegalArgumentException e) { + unMappedTypes.add(classType); + } + } + if (matchFound) { + return deserializedObject; + } + if (isOneOf) { + throw new OneOfValidationException(unMappedTypes, json); + } + throw new AnyOfValidationException(unMappedTypes, json); + } + + private static String getTypeCombinatorCaseType(Class clazz) { + TypeCombinatorCase caseAnnotation = clazz.getAnnotation(TypeCombinatorCase.class); + if (caseAnnotation == null) { + return ""; + } + return caseAnnotation.type(); + } + + /** + * Json deserialization of the given Json string. + * + * @param json The Json string to deserialize. + * @return The deserialized Json as a Map. + * @throws IOException Signals if any I/O exception occurred. + */ + public static LinkedHashMap deserialize(String json) throws IOException { + if (isNullOrWhiteSpace(json)) { + return null; + } + + TypeReference> typeRef = new TypeReference>() { + }; + return deserialize(json, typeRef); + } + + /** + * JSON Deserialization of the given json string. + * + * @param json The json string to deserialize. + * @param typeReference TypeReference of T. + * @param The type of the object to deserialize into. + * @return The deserialized object. + * @throws IOException Signals if any I/O exception occurred. + */ + public static T deserialize(String json, TypeReference typeReference) + throws IOException { + if (isNullOrWhiteSpace(json)) { + return null; + } + + return mapper.readValue(json, typeReference); + } + + /** + * JSON Deserialization of the given json string with FAIL_ON_UNKNOWN_PROPERTIES + * flag as true. + * + * @param jsonNode The Json Node to deserialize. + * @param typeReference TypeReference of T. + * @param The type of the object to deserialize into. + * @return The deserialized object. + * @throws IOException Signals if any I/O exception occurred. + */ + public static T deserialize(JsonNode jsonNode, + TypeReference typeReference) throws IOException { + if (jsonNode == null) { + return null; + } + + return strictMapper.convertValue(jsonNode, typeReference); + } + + /** + * JSON deserialization of the given JsonNode with FAIL_ON_UNKNOWN_PROPERTIES + * flag as true. + * + * @param The type of the object to deserialize into. + * @param jsonNode The Json Node to deserialize. + * @param clazz The type of the object to deserialize into. + * @return The deserialized object. + * @throws IOException Signals if any I/O exception occurred. + */ + public static T deserialize(JsonNode jsonNode, Class clazz) + throws IOException { + if (jsonNode == null) { + return null; + } + return strictMapper.convertValue(jsonNode, clazz); + } + + /** + * XML Deserialization of the given xml string. + * + * @param The class of the object to deserialize into. + * @param xml The xml string to deserialize. + * @param cls The class of the object to deserialize into. + * @return The deserialized object. + * @throws IOException Signals if any I/O exception occurred. + */ + public static T deserializeXml(String xml, Class cls) throws IOException { + try { + JAXBContext jaxbContext = JAXBContext.newInstance(cls); + Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); + StringReader reader = new StringReader(xml); + JAXBElement jaxbElement = jaxbUnmarshaller.unmarshal(new StreamSource(reader), cls); + + return jaxbElement.getValue(); + } catch (JAXBException jaxbException) { + throw new IOException(jaxbException); + } + } + + /** + * XML Deserialization of the given xml string. + * + * @param The class of the object to deserialize into. + * @param xml The xml string to deserialize. + * @param cls The class of the object to deserialize into. + * @return The deserialized object list. + * @throws IOException Signals if any I/O exception occurred. + */ + public static List deserializeXmlArray(String xml, Class cls) + throws IOException { + try { + JAXBContext jaxbContext = JAXBContext.newInstance(cls); + Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); + StringReader reader = new StringReader(xml); + JAXBElement jaxbElement = jaxbUnmarshaller.unmarshal(new StreamSource(reader), + cls); + + return Arrays.asList(jaxbElement.getValue()); + } catch (JAXBException jaxbException) { + throw new IOException(jaxbException); + } + } + + /** + * XML Deserialization of the given xml string for simple types. + * + * @param The class of the object to deserialize into. + * @param xml The xml string to deserialize. + * @param cls The class of the object to deserialize into. + * @return The deserialized simple types object list. + * @throws IOException Signals if any I/O exception occurred. + */ + public static List deserializeXmlSimpleTypesArray(String xml, + Class cls) throws IOException { + try { + JAXBContext jaxbContext = JAXBContext.newInstance(cls); + Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); + List deserializedList = new ArrayList<>(); + Pattern pattern = Pattern.compile("<.+?>(.+?)"); + Matcher patternMatcher = pattern.matcher(xml); + while (patternMatcher.find()) { + StringReader reader = new StringReader(patternMatcher.group()); + T unmarshalledElement = jaxbUnmarshaller.unmarshal(new StreamSource(reader), cls) + .getValue(); + deserializedList.add(unmarshalledElement); + } + return deserializedList; + } catch (JAXBException jaxbException) { + throw new IOException(jaxbException); + } + } + + /** + * JSON Deserialization from custom deserializer based on given discriminator + * and registry. + * + * @param jp Parsed used for reading JSON content. + * @param ctxt Context that can be used to access + * information about this deserialization + * activity. + * @param discriminator The model's discriminator. + * @param registry The map containing all discriminators as + * keys and associated classes as values. + * @param typesWithoutDiscriminator The list containing all types without + * discriminators. + * @param isOneOf The boolean flag to validate for oneOf flow. + * @param the deserialized response type. + * @return The deserialized object. + * @throws IOException Signals if any I/O exception occurred. + */ + public static T deserialize(JsonParser jp, DeserializationContext ctxt, + String discriminator, List>> registry, + List> typesWithoutDiscriminator, boolean isOneOf) + throws IOException { + ObjectCodec oc = jp.getCodec(); + JsonNode jsonNode = oc.readTree(jp); + List> types = deduceType(jsonNode, discriminator, registry); + + if (types == null || types.isEmpty()) { + if (typesWithoutDiscriminator != null && !typesWithoutDiscriminator.isEmpty()) { + types = typesWithoutDiscriminator; + } else { + throw new IOException("Discriminator is missing."); + } + } + + return deserialize(jsonNode, types, isOneOf); + } + + /** + * Json deserialization of the given Json string. + * + * @param json The Json string to deserialize. + * @return The deserialized Json as an Object. + */ + public static Object deserializeAsObject(String json) { + if (isNullOrWhiteSpace(json)) { + return null; + } + try { + return CoreHelper.deserialize(json, new TypeReference() { + }); + } catch (IOException e) { + // Failed to deserialize when json is not representing a JSON object. + // i.e. either its string or any primitive type. + return json; + } + } + + /** + * JSON Deserialization of the given json string. + * + * @param The type of the object to deserialize into. + * @param json The Json string to deserialize. + * @param classArray The class of the array of objects to deserialize into. + * @return The deserialized list of objects. + * @throws IOException Signals if any I/O exception occurred.. + */ + public static List deserializeArray(String json, Class classArray) + throws IOException { + if (isNullOrWhiteSpace(json)) { + return null; + } + + return Arrays.asList(mapper.readValue(json, classArray)); + } + + /** + * Replaces template parameters in the given URL. + * + * @param queryBuilder The query string builder to replace the template + * parameters. + * @param parameters The parameters to replace in the URL. + */ + public static void appendUrlWithTemplateParameters(StringBuilder queryBuilder, + Map> parameters) { + // Perform parameter validation + if (null == queryBuilder) { + throw new IllegalArgumentException( + "Given value for parameter \"queryBuilder\" is invalid."); + } + + if (null == parameters) { + return; + } + + // Iterate and append parameters + for (Map.Entry> pair : parameters.entrySet()) { + + String replaceValue = ""; + Object element = pair.getValue().getKey(); + boolean shouldEncode = pair.getValue().getValue(); + + // Load element value as string + if (null == element) { + replaceValue = ""; + } else if (element instanceof Collection) { + replaceValue = flattenCollection("", (Collection) element, shouldEncode, + "%s%s%s", '/'); + } else { + if (shouldEncode) { + replaceValue = tryUrlEncode(element.toString(), false); + } else { + replaceValue = element.toString(); + } + } + + // Find the template parameter and replace it with its value + replaceAll(queryBuilder, "{" + pair.getKey() + "}", replaceValue); + } + } + + /** + * Appends the given set of parameters to the given query string. + * + * @param queryBuilder The query URL string to append the + * parameters. + * @param parameters The parameters to append. + * @param arraySerializationFormat the array serialization format. + */ + public static void appendUrlWithQueryParameters(StringBuilder queryBuilder, + Map parameters, ArraySerializationFormat arraySerializationFormat) { + // Perform parameter validation + if (queryBuilder == null) { + throw new IllegalArgumentException( + "Given value for parameter \"queryBuilder\" is invalid."); + } + if (parameters == null || parameters.isEmpty()) { + return; + } + + // Check if query string already has parameters + boolean hasParams = queryBuilder.indexOf("?") > 0; + queryBuilder.append(hasParams ? '&' : '?'); + + encodeObjectAsQueryString("", parameters, queryBuilder, arraySerializationFormat); + } + + /** + * Validates if the string is null, empty or whitespace. + * + * @param s The string to validate. + * @return The result of validation. + */ + public static boolean isNullOrWhiteSpace(String s) { + if (s == null) { + return true; + } + + int length = s.length(); + if (length > 0) { + for (int start = 0, middle = length / 2, end = length + - 1; start <= middle; start++, end--) { + if (s.charAt(start) > ' ' || s.charAt(end) > ' ') { + return false; + } + } + return true; + } + return false; + } + + /** + * Replaces all occurrences of the given string in the string builder. + * + * @param stringBuilder The string builder to update with replaced strings. + * @param toReplace The string to replace in the string builder. + * @param replaceWith The string to replace with. + */ + public static void replaceAll(StringBuilder stringBuilder, String toReplace, + String replaceWith) { + int index = stringBuilder.indexOf(toReplace); + + while (index != -1) { + stringBuilder.replace(index, index + toReplace.length(), replaceWith); + index += replaceWith.length(); // Move to the end of the replacement + index = stringBuilder.indexOf(toReplace, index); + } + } + + /** + * Updates the user agent header value. + * + * @param apiUserAgent the String value of apiUserAgent. + * @param userAgentConfig the Map of user agent config. + * @return {@link String}. + */ + public static String updateUserAgent(String apiUserAgent, Map userAgentConfig) { + String engineVersion = System.getProperty("java.runtime.version"); + String osName = System.getProperty("os.name") + "-" + System.getProperty("os.version"); + userAgent = apiUserAgent; + userAgent = userAgent.replace("{engine}", "JRE"); + userAgent = userAgent.replace("{engine-version}", + engineVersion != null ? engineVersion : ""); + userAgent = userAgent.replace("{os-info}", osName != null ? osName : ""); + + if (userAgentConfig != null) { + userAgentConfig.forEach((key, value) -> { + userAgent = userAgent.replace(key, value); + }); + } + + return userAgent; + } + + /** + * Removes null values from the given map. + * + * @param map Map of values. + */ + public static void removeNullValues(Map map) { + if (map == null) { + return; + } + + map.values().removeAll(Collections.singleton(null)); + } + + /** + * Validates and processes the given URL. + * + * @param url The given URL to process. + * @return Pre-process URL as string. + */ + public static String cleanUrl(StringBuilder url) { + // Ensures that the URLs are absolute + Pattern pattern = Pattern.compile("^(https?://[^/]+)"); + Matcher matcher = pattern.matcher(url); + if (!matcher.find()) { + throw new IllegalArgumentException("Invalid Url format."); + } + + // Get the http protocol match + String protocol = matcher.group(1); + + // Removes redundant forward slashes + String query = url.substring(protocol.length()); + query = query.replaceAll("//+", "/"); + + // Returns processed URL + return protocol.concat(query); + } + + /** + * Prepares Array style form fields from a given array of values. + * + * @param value Value for the form fields. + * @param arraySerializationFormat serialization format. + * @return Dictionary of form fields created from array elements. + */ + public static List> prepareFormFields(Map value, + ArraySerializationFormat arraySerializationFormat) { + List> formFields = new ArrayList<>(); + if (value != null) { + objectToList("", value, formFields, new HashSet(), arraySerializationFormat); + } + return formFields; + } + + /** + * JSON Deserialization of the given json string with FAIL_ON_UNKNOWN_PROPERTIES + * flag as true. + * + * @param The type of the object to deserialize into. + * @param json The Json string to deserialize. + * @param classArray The class of the array of objects to deserialize into. + * @return The deserialized list of objects. + * @throws IOException Signals if any I/O exception occurred. + */ + public static List deserializeArray(JsonNode json, Class classArray) + throws IOException { + if (json == null) { + return null; + } + + return Arrays.asList(strictMapper.convertValue(json, classArray)); + } + + /** + * Deduces the type based on given discriminator and registry. + * + * @param jsonNode The json to check against. + * @param discriminator The model's discriminator. + * @param registry The Map containing all discriminators as keys and + * associated classes as values. + * @param The type of the object to deserialize into. + * @return The type to deserialize into. + * @throws IOException Signals if any I/O exception occurred. + */ + private static List> deduceType(JsonNode jsonNode, String discriminator, + List>> registry) throws IOException { + if (jsonNode == null || registry == null) { + return null; + } + + final String discriminatorValue; + if (jsonNode.isArray()) { + if (jsonNode.has(0) && jsonNode.get(0).has(discriminator)) { + // JSON is an array of model objects + discriminatorValue = jsonNode.get(0).get(discriminator).asText(); + } else { + discriminatorValue = deduceTypeFromImmidiateChild(jsonNode.get(0), discriminator); + } + } else { + if (jsonNode.has(discriminator)) { + // JSON is a model object + discriminatorValue = jsonNode.get(discriminator).asText(); + } else { + // JSON is a Map so deduce discriminator for first child only + discriminatorValue = deduceTypeFromImmidiateChild(jsonNode, discriminator); + } + } + + return registry.stream().filter(item -> item.get(discriminatorValue) != null) + .map(item -> item.get(discriminatorValue)).distinct().collect(Collectors.toList()); + } + + /** + * Deduces the type from immediate child if exists based on given discriminator. + * + * @param jsonNode The json to check against. + * @param discriminator The model's discriminator. + * @return The type to deserialize into. + * @throws IOException Signals if any I/O exception occurred. + */ + private static String deduceTypeFromImmidiateChild(JsonNode jsonNode, String discriminator) { + Iterator iterator = jsonNode.iterator(); + while (iterator.hasNext()) { + JsonNode tempNode = iterator.next(); + if (tempNode.isArray()) { + if (tempNode.has(0) && tempNode.get(0).has(discriminator)) { + // JSON is an array of model objects + return tempNode.get(0).get(discriminator).asText(); + } + } else { + if (tempNode.has(discriminator)) { + // JSON is a model object + return tempNode.get(discriminator).asText(); + } + } + } + return null; + } + + /** + * Encodes a given object to URL encoded string. + * + * @param name Name of the object. + * @param obj Raw object sent from caller. + * @param objBuilder String of elements. + * @param arraySerializationFormat The array serialization format. + */ + private static void encodeObjectAsQueryString(String name, Object obj, StringBuilder objBuilder, + ArraySerializationFormat arraySerializationFormat) { + + List> objectList = new ArrayList<>(); + objectToList(name, obj, objectList, new HashSet(), arraySerializationFormat); + boolean hasParam = false; + List arrays = new ArrayList(); + + for (SimpleEntry pair : objectList) { + String accessor = pair.getKey(); + // Ignore null + Object value = pair.getValue(); + if (value == null) { + continue; + } + + hasParam = true; + // Load element value as string + + if (accessor.matches(".*?\\[\\d+\\]$") && isDelimeterFormat(arraySerializationFormat)) { + + String arrayName = accessor.substring(0, accessor.lastIndexOf('[')); + + if (arrays.contains(arrayName)) { + objBuilder.setLength(objBuilder.length() - 1); + accessor = getAccessorStringFormat(arraySerializationFormat); + } else { + accessor = arrayName + "="; + } + + if (!arrays.contains(arrayName)) { + arrays.add(arrayName); + } + + appendParamKeyValuePair("%s%s&", objBuilder, accessor, value); + } else { + appendParamKeyValuePair("%s=%s&", objBuilder, accessor, value); + } + } + + // Removing the last & + if (hasParam) { + objBuilder.setLength(objBuilder.length() - 1); + } + } + + private static void appendParamKeyValuePair(String formatString, StringBuilder objBuilder, + String accessor, Object value) { + + String paramKeyValPair = String.format(formatString, accessor, + tryUrlEncode(value.toString(), false)); + objBuilder.append(paramKeyValPair); + } + + /** + * Flattening a collection of objects into a string. + * + * @param elemName The element name of collection. + * @param array Array of elements to flatten. + * @param encode Need to encode?. + * @param fmt Format string to use for array flattening. + * @param separator Separator to use for string concatenation. + * @return Representative string made up of array elements. + */ + private static String flattenCollection(String elemName, Collection array, boolean encode, + String fmt, char separator) { + StringBuilder builder = new StringBuilder(); + + // Append all elements of the array into a string + for (Object element : array) { + String elemValue = null; + + // Replace null values with empty string to maintain index order + if (element == null) { + elemValue = ""; + } else { + elemValue = element.toString(); + } + if (encode) { + elemValue = tryUrlEncode(elemValue, false); + } + builder.append(String.format(fmt, elemName, elemValue, separator)); + } + + // Remove the last separator, if appended + if ((builder.length() > 1) && (builder.charAt(builder.length() - 1) == separator)) { + builder.deleteCharAt(builder.length() - 1); + } + + return builder.toString(); + } + + /** + * Tries URL encode using UTF-8. + * + * @param value The value to URL encode. + * @param spaceAsPercentEncoded The flag get space character as percent encoded. + * @return Encoded url. + */ + public static String tryUrlEncode(String value, boolean spaceAsPercentEncoded) { + try { + String encodedUrl = URLEncoder.encode(value, "UTF-8"); + if (spaceAsPercentEncoded) { + return encodedUrl.replace("+", "%20"); + } + return encodedUrl; + } catch (UnsupportedEncodingException ex) { + return value; + } + } + + /** + * Responsible to encode into base64 the username and password + * + * @param basicAuthUserName The auth username + * @param basicAuthPassword The auth password + * @return The base64 encoded String + */ + public static String getBase64EncodedCredentials(String basicAuthUserName, + String basicAuthPassword) { + String authCredentials = basicAuthUserName + ":" + basicAuthPassword; + return "Basic " + Base64.getEncoder().encodeToString(authCredentials.getBytes()); + } + + private static void objectToList(String objName, Collection obj, + List> objectList, HashSet processed, + ArraySerializationFormat arraySerializationFormat) { + + Collection array = obj; + // Append all elements of the array into a string + int index = 0; + for (Object element : array) { + // load key value pair + String key; + + if (isWrapperType(element) + && (arraySerializationFormat == ArraySerializationFormat.UNINDEXED + || arraySerializationFormat == ArraySerializationFormat.PLAIN)) { + key = arraySerializationFormat == ArraySerializationFormat.UNINDEXED + ? String.format("%s[]", objName) + : objName; + } else { + key = String.format("%s[%d]", objName, index++); + } + loadKeyValuePairForEncoding(key, element, objectList, processed, + arraySerializationFormat); + } + + } + + private static void objectToList(String objName, Map obj, + List> objectList, HashSet processed, + ArraySerializationFormat arraySerializationFormat) { + // Process map + Map map = obj; + // Append all elements of the array into a string + for (Map.Entry pair : map.entrySet()) { + String attribName = pair.getKey().toString(); + if ((objName != null) && (!objName.isEmpty())) { + attribName = String.format("%s[%s]", objName, attribName); + } + loadKeyValuePairForEncoding(attribName, pair.getValue(), objectList, processed, + arraySerializationFormat); + } + } + + /** + * Converts a given object to a form encoded map. + * + * @param objName Name of the object. + * @param obj The object to convert into a map. + * @param objectList The object list to populate. + * @param processed List of object hashCodes that are already + * parsed. + * @param arraySerializationFormat The array serialization format. + */ + private static void objectToList(String objName, Object obj, + List> objectList, HashSet processed, + ArraySerializationFormat arraySerializationFormat) { + // Null values need not to be processed + if (obj == null) { + return; + } + + // Wrapper types are autoboxed, so reference checking is not needed + Class clazz = obj.getClass(); + + Annotation typeCombinatorAnnotation = clazz.getAnnotation(TypeCombinatorCase.class); + if (!isWrapperType(clazz) && typeCombinatorAnnotation == null) { + // Avoid infinite recursion + if (processed.contains(objName.hashCode())) { + return; + } + processed.add(objName.hashCode()); + } + + // Process arrays + if (obj instanceof Collection) { + objectToList(objName, (Collection) obj, objectList, processed, + arraySerializationFormat); + } else if (obj.getClass().isArray()) { + // Process array + + Object[] array = (Object[]) obj; + // Append all elements in the array into a string + int index = 0; + for (Object element : array) { + // Load key value pair + String key = String.format("%s[%d]", objName, index++); + loadKeyValuePairForEncoding(key, element, objectList, processed, + arraySerializationFormat); + } + } else if (obj instanceof Map) { + objectToList(objName, (Map) obj, objectList, processed, arraySerializationFormat); + } else { + // Process objects + if (typeCombinatorAnnotation != null) { + for (Field field : clazz.getDeclaredFields()) { + // unexpected field $jococoData came for unit test coverage and makes test fails + if (field.getName() == "$jacocoData") { + continue; + } + + Object fieldValue = null; + Annotation serializeAnnotation = null; + try { + field.setAccessible(true); + fieldValue = field.get(obj); + serializeAnnotation = field.getAnnotation(JsonSerialize.class); + if (serializeAnnotation == null) { + serializeAnnotation = field.getAnnotation(FormSerialize.class); + } + } catch (IllegalArgumentException | IllegalAccessException e) { + // Ignoring the exception + } + + if (serializeAnnotation != null) { + if (serializeAnnotation instanceof JsonSerialize) { + loadKeyValuePairForEncoding(objName, fieldValue, objectList, processed, + (JsonSerialize) serializeAnnotation, arraySerializationFormat); + } else { + loadKeyValuePairForEncoding(objName, fieldValue, objectList, processed, + (FormSerialize) serializeAnnotation, arraySerializationFormat); + } + } else { + loadKeyValuePairForEncoding(objName, fieldValue, objectList, processed, + arraySerializationFormat); + } + } + return; + } + // Invoke getter methods + while (clazz != null) { + for (Method method : clazz.getDeclaredMethods()) { + + // Is a public/protected getter or internalGetter? + if (method.getParameterTypes().length != 0 + || Modifier.isPrivate(method.getModifiers()) + || (!method.getName().startsWith("get") + && !method.getName().startsWith("internalGet"))) { + continue; + } + + // Get JsonGetter annotation + Annotation getterAnnotation = method.getAnnotation(JsonGetter.class); + if (getterAnnotation == null) { + continue; + } + + // Load key name from getter attribute name + String attribName = ((JsonGetter) getterAnnotation).value(); + if ((objName != null) && (!objName.isEmpty())) { + attribName = String.format("%s[%s]", objName, attribName); + } + + try { + // Load value by invoking getter method + method.setAccessible(true); + Object value = method.invoke(obj); + JsonSerialize serializerAnnotation = method + .getAnnotation(JsonSerialize.class); + // Load key value pair into objectList + if (serializerAnnotation != null) { + loadKeyValuePairForEncoding(attribName, value, objectList, processed, + serializerAnnotation, arraySerializationFormat); + } else { + loadKeyValuePairForEncoding(attribName, value, objectList, processed, + arraySerializationFormat); + } + } catch (IllegalAccessException | IllegalArgumentException + | InvocationTargetException e) { + // This block only calls getter methods. + // These getters don't throw any exception except invocationTargetException. + // The getters are public so there is no chance of an IllegalAccessException + // Steps we've followed ensure that the object has the specified method. + } + } + clazz = clazz.getSuperclass(); + } + } + } + + private static String getAccessorStringFormat( + ArraySerializationFormat arraySerializationFormat) { + switch (arraySerializationFormat) { + case CSV: + return CSV_FORMAT; + case PSV: + return PSV_FORMAT; + case TSV: + return TSV_FORMAT; + default: + return ""; + } + } + + private static boolean isDelimeterFormat(ArraySerializationFormat arraySerializationFormat) { + return (arraySerializationFormat == ArraySerializationFormat.CSV + || arraySerializationFormat == ArraySerializationFormat.TSV + || arraySerializationFormat == ArraySerializationFormat.PSV); + } + + /** + * Processes the value and load into objectList against key. + * + * @param key The key to used for creation of key value + * pair. + * @param value The value to process against the given key. + * @param objectList The object list to process with key value + * pair. + * @param processed List of processed objects hashCodes. + * @param serializer The serializer for serialize the object. + * @param arraySerializationFormat The array serialization format. + * @throws JsonProcessingException Signals that a Json Processing Exception has + * occurred. + */ + private static void loadKeyValueUsingSerializer(String key, Object value, + List> objectList, HashSet processed, + JsonSerializer serializer, ArraySerializationFormat arraySerializationFormat) + throws JsonProcessingException { + value = serialize(value, serializer); + + Object obj = deserializeAsObject(value.toString()); + if (obj instanceof List || obj instanceof Map) { + loadKeyValuePairForEncoding(key, obj, objectList, processed, arraySerializationFormat); + } else { + if (value.toString().startsWith("\"")) { + value = value.toString().substring(1, value.toString().length() - 1); + } + objectList.add(new SimpleEntry(key, value)); + } + } + + /** + * While processing objects to map, loads value after serializing. + * + * @param key The key to used for creation of key value + * pair. + * @param value The value to process against the given key. + * @param objectList The object list to process with key value + * pair. + * @param processed List of processed objects hashCodes. + * @param formSerializerAnnotation Annotation for serializer. + * @param arraySerializationFormat The array serialization format. + */ + private static void loadKeyValuePairForEncoding(String key, Object value, + List> objectList, HashSet processed, + FormSerialize formSerializerAnnotation, + ArraySerializationFormat arraySerializationFormat) { + if (value == null) { + return; + } + + try { + JsonSerializer serializer = getCollectionCustomSerializer(formSerializerAnnotation); + loadKeyValueUsingSerializer(key, value, objectList, processed, serializer, + arraySerializationFormat); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + + /** + * While processing objects to map, decides whether to perform recursion or load + * value. + * + * @param key The key for creating key value pair. + * @param value The value to process against the given key. + * @param objectList The object list to process with key value + * pair. + * @param processed List of processed objects hashCodes. + * @param arraySerializationFormat The array serialization format. + */ + private static void loadKeyValuePairForEncoding(String key, Object value, + List> objectList, HashSet processed, + ArraySerializationFormat arraySerializationFormat) { + if (value == null) { + return; + } + if (isWrapperType(value)) { + objectList.add(new SimpleEntry(key, value)); + } else if (value instanceof CoreJsonObject) { + objectToList(key, ((CoreJsonObject) value).getStoredObject(), objectList, processed, + arraySerializationFormat); + } else if (value instanceof CoreJsonValue) { + Object storedValue = ((CoreJsonValue) value).getStoredObject(); + if (isWrapperType(storedValue)) { + objectList.add(new SimpleEntry(key, storedValue)); + } else { + objectToList(key, storedValue, objectList, processed, arraySerializationFormat); + } + } else if (value instanceof UUID) { + // UUIDs can be converted to string + objectList.add(new SimpleEntry(key, value.toString())); + } else { + objectToList(key, value, objectList, processed, arraySerializationFormat); + } + } + + /** + * While processing objects to map, loads value after serializing. + * + * @param key The key to used for creation of key value + * pair. + * @param value The value to process against the given key. + * @param objectList The object list to process with key value + * pair. + * @param processed List of processed objects hashCodes. + * @param serializerAnnotation Annotation for serializer. + * @param arraySerializationFormat The array serialization format. + */ + private static void loadKeyValuePairForEncoding(String key, Object value, + List> objectList, HashSet processed, + JsonSerialize serializerAnnotation, ArraySerializationFormat arraySerializationFormat) { + if (value == null) { + return; + } + + try { + JsonSerializer serializer = getSerializer(serializerAnnotation); + if (serializer == null) { + serializer = getCollectionSerializer(serializerAnnotation); + } + + loadKeyValueUsingSerializer(key, value, objectList, processed, serializer, + arraySerializationFormat); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + + /** + * Check if the given object can be wrapped directly. + * + * @param object The given object. + * @return true if the class is an autoboxed class e.g., Integer. + */ + private static boolean isWrapperType(Object object) { + if (object == null) { + return false; + } + return WRAPPER_TYPES.contains(object.getClass()) || object.getClass().isPrimitive() + || object.getClass().isEnum(); + } + + /** + * Json Serialization of an ENUM defined under oneOf/anyOf container. + * + * @param value The object to serialize into Json String. + * @return The serialized Json String representation of the given object. + * @throws JsonProcessingException Signals that a Json Processing Exception has + * occurred. + */ + public static String serializeEnumContainer(Object value) throws JsonProcessingException { + if (value instanceof String || value instanceof Integer) { + return String.valueOf(value); + } + + return serialize(value); + } + + /** + * Custom deserializer class of any string property for disallowing implicit + * type conversion. + */ + private static class CoercionLessStringDeserializer extends StringDeserializer { + private static final long serialVersionUID = 1L; + + @Override + public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + + if (p.getCurrentToken() != JsonToken.VALUE_STRING) { + String message = "Cannot coerce " + p.getCurrentToken() + " to String value"; + throw MismatchedInputException.from(p, String.class, message); + } + return super.deserialize(p, ctxt); + } + } } diff --git a/src/test/java/apimatic/core/utilities/CoreHelperTest.java b/src/test/java/apimatic/core/utilities/CoreHelperTest.java index 2e82baa5..05e6af31 100644 --- a/src/test/java/apimatic/core/utilities/CoreHelperTest.java +++ b/src/test/java/apimatic/core/utilities/CoreHelperTest.java @@ -72,31 +72,27 @@ public class CoreHelperTest { private static final long UNIQUE_UUID_NUMBER2 = 87866L; private static final long UNIQUE_UUID_NUMBER1 = 876547L; private static final List LIST_OF_INTEGERS = Arrays.asList(1, 2, 3, 4, 5); - private static final String XML_ARRAY = - "\r\n" + " \r\n" - + " 6\r\n" + " Data\r\n" + "\r\n" - + ""; - private static final String INVALID_XML = - "\r\n" + " item number=\"3\" string=\"XMLRootName\"\r\n" + " number>6\r\n" - + " Data\r\n" + "\r\n" + ""; - private static final String XML = - "\r\n" - + "\r\n" - + " 6\r\n" + " Data\r\n" - + "\r\n" + ""; - - private static final String JSON_OBJECT = - "https://localhost:3000/query?operations[$id]=https%3A%2F%2Fexample.com%2Fperson." - + "schema.json&operations[$schema]=https%3A%2F%2Fjson-schema" - + ".org%2Fdraft%2F2020-12%2Fschema&operations[title]=Person&operations" - + "[type]=object&operations[properties][firstName][type]=string&operations" - + "[properties][firstName][description]=The+person%27s+first+name.&" - + "operations[properties][lastName][type]=string&operations[properties]" - + "[lastName][description]=The+person%27s+last+name.&operations" - + "[properties][age][type]=integer&operations[properties][age][description]" - + "=Age+in+years&operations[properties][age][minimum]=0"; - private static final String JSON_VALUE = - "https://localhost:3000/query?operations=test-JsonValue"; + private static final String XML_ARRAY = "\r\n" + + " \r\n" + " 6\r\n" + + " Data\r\n" + "\r\n" + ""; + private static final String INVALID_XML = "\r\n" + + " item number=\"3\" string=\"XMLRootName\"\r\n" + " number>6\r\n" + + " Data\r\n" + "\r\n" + ""; + private static final String XML = "\r\n" + + "\r\n" + + " 6\r\n" + " Data\r\n" + + "\r\n" + ""; + + private static final String JSON_OBJECT = "https://localhost:3000/query?operations[$id]=https%3A%2F%2Fexample.com%2Fperson." + + "schema.json&operations[$schema]=https%3A%2F%2Fjson-schema" + + ".org%2Fdraft%2F2020-12%2Fschema&operations[title]=Person&operations" + + "[type]=object&operations[properties][firstName][type]=string&operations" + + "[properties][firstName][description]=The+person%27s+first+name.&" + + "operations[properties][lastName][type]=string&operations[properties]" + + "[lastName][description]=The+person%27s+last+name.&operations" + + "[properties][age][type]=integer&operations[properties][age][description]" + + "=Age+in+years&operations[properties][age][minimum]=0"; + private static final String JSON_VALUE = "https://localhost:3000/query?operations=test-JsonValue"; @Test public void testSerializeNullObject() throws JsonProcessingException { @@ -104,7 +100,6 @@ public void testSerializeNullObject() throws JsonProcessingException { assertNull(CoreHelper.serialize(obj)); } - @Test public void testIsWhiteSpace() { String whiteSpaceString = " "; @@ -134,7 +129,6 @@ public void testBase64Encoding() { String username = "username"; String password = "password"; - String expectedEncodedString = "Basic dXNlcm5hbWU6cGFzc3dvcmQ="; String actualEncodedString = CoreHelper.getBase64EncodedCredentials(username, password); assertEquals(actualEncodedString, expectedEncodedString); @@ -162,7 +156,6 @@ public void testInvalidUrlEncoding() { assertEquals(actual, expected); } - @Test public void testUrlEncodingPercentage() { String urlString = "https://localhost:8080/query=0"; @@ -182,7 +175,6 @@ public void testCleanUrl() { stringBuilder.append(server); stringBuilder.append(path); - String expected = "https://localhost:3000/query/basic/body"; String actual = CoreHelper.cleanUrl(stringBuilder); @@ -225,9 +217,8 @@ public void testRemoveNullValues1() { public void testUpdateUserAgent() { String userAgent = "Java|31.8.0|{engine}|{engine-version}|{os-info}"; - String expected = - "Java|31.8.0|JRE|" + System.getProperty("java.runtime.version") + "|" - + System.getProperty("os.name") + "-" + System.getProperty("os.version"); + String expected = "Java|31.8.0|JRE|" + System.getProperty("java.runtime.version") + "|" + + System.getProperty("os.name") + "-" + System.getProperty("os.version"); String actual = CoreHelper.updateUserAgent(userAgent, null); @@ -240,17 +231,15 @@ public void testUpdateUserAgent1() { Map userAgentConfig = new HashMap<>(); userAgentConfig.put("{square-version}", "17.2.6"); - String expected = - "Java|31.8.0|JRE|" + System.getProperty("java.runtime.version") + "|" - + System.getProperty("os.name") + "-" + System.getProperty("os.version") - + "|17.2.6"; + String expected = "Java|31.8.0|JRE|" + System.getProperty("java.runtime.version") + "|" + + System.getProperty("os.name") + "-" + System.getProperty("os.version") + + "|17.2.6"; String actual = CoreHelper.updateUserAgent(userAgent, userAgentConfig); assertEquals(actual, expected); } - @Test public void testAppendTemplateParameters() { String baseUri = "https://localhost:3000"; @@ -285,7 +274,6 @@ public void testAppendTemplateParameters1() { assertEquals(actual, expected); } - @Test public void testAppendTemplateParameters2() { String baseUri = "https://localhost:3000"; @@ -320,7 +308,6 @@ public void testAppendTemplateParameters3() { assertEquals(actual, expected); } - @Test(expected = IllegalArgumentException.class) public void testAppendTemplateParameters4() { String templateStringValue = "strings"; @@ -333,7 +320,6 @@ public void testAppendTemplateParameters4() { CoreHelper.appendUrlWithTemplateParameters(queryBuilder, templateParameters); } - @Test public void testAppendTemplateParametersShouldNotEncode() { String baseUri = "https://localhost:3000"; @@ -351,7 +337,6 @@ public void testAppendTemplateParametersShouldNotEncode() { assertEquals(actual, expected); } - @Test public void testAppendTemplateParameters5() { String baseUri = "https://localhost:3000"; @@ -365,11 +350,10 @@ public void testAppendTemplateParameters5() { assertEquals(actual, expected); } - @Test public void testptionalNullable() throws IOException { - ChildClass child = - CoreHelper.deserialize( + ChildClass child = CoreHelper + .deserialize( "{\"Grand_Parent_Required_Nullable\":null,\"Grand_Parent_Required\":" + "\"not nullable and required\",\"class\":23,\"" + "Parent_Optional_Nullable_With_Default_Value\":" @@ -381,14 +365,13 @@ public void testptionalNullable() throws IOException { + "nullable and required\",\"Child_Class_Array\":null}", ChildClass.class); - String expected = - "{\"Grand_Parent_Required_Nullable\":null,\"Grand_Parent_Required\":" - + "\"not nullable and required\",\"Parent_Optional_Nullable_With_" - + "Default_Value\":\"Has default value\",\"Parent_Required_Nullable" - + "\":null,\"Parent_Required\":\"not nullable and required\"," - + "\"Optional_Nullable\":null,\"Optional_Nullable_With_Default_Value\":" - + "\"With default value\",\"Required_Nullable\":null,\"Required\":" - + "\"not nullable and required\",\"Child_Class_Array\":null,\"class\":23}"; + String expected = "{\"Grand_Parent_Required_Nullable\":null,\"Grand_Parent_Required\":" + + "\"not nullable and required\",\"Parent_Optional_Nullable_With_" + + "Default_Value\":\"Has default value\",\"Parent_Required_Nullable" + + "\":null,\"Parent_Required\":\"not nullable and required\"," + + "\"Optional_Nullable\":null,\"Optional_Nullable_With_Default_Value\":" + + "\"With default value\",\"Required_Nullable\":null,\"Required\":" + + "\"not nullable and required\",\"Child_Class_Array\":null,\"class\":23}"; String actual = CoreHelper.serialize(child); assertEquals(actual, expected); @@ -619,8 +602,6 @@ public void testCommaSeperated() throws IOException { assertTrue(actual.contains(",")); } - - @Test(expected = IllegalArgumentException.class) public void testAppendQueryParameters4() { String queryValue = "x+y"; @@ -632,7 +613,6 @@ public void testAppendQueryParameters4() { ArraySerializationFormat.INDEXED); } - @Test public void testPrepareFormFields() { String body = "test for prepare form fields"; @@ -641,8 +621,8 @@ public void testPrepareFormFields() { List> expected = new ArrayList>(); expected.add(new SimpleEntry("body", body)); - List> actual = - CoreHelper.prepareFormFields(formParameters, ArraySerializationFormat.INDEXED); + List> actual = CoreHelper.prepareFormFields(formParameters, + ArraySerializationFormat.INDEXED); assertEquals(actual, expected); } @@ -654,12 +634,11 @@ public void testPrepareFormFieldsUUID() { List> expected = new ArrayList>(); expected.add(new SimpleEntry("body", body.toString())); - List> actual = - CoreHelper.prepareFormFields(formParameters, ArraySerializationFormat.INDEXED); + List> actual = CoreHelper.prepareFormFields(formParameters, + ArraySerializationFormat.INDEXED); assertEquals(actual, expected); } - @Test public void testPrepareFormFieldsModel() { DeleteBody body = new DeleteBody.Builder().name("ali").field("QA").build(); @@ -669,12 +648,11 @@ public void testPrepareFormFieldsModel() { List> expected = new ArrayList>(); expected.add(new SimpleEntry("body[name]", "ali")); expected.add(new SimpleEntry("body[field]", "QA")); - List> actual = - CoreHelper.prepareFormFields(formParameters, ArraySerializationFormat.INDEXED); + List> actual = CoreHelper.prepareFormFields(formParameters, + ArraySerializationFormat.INDEXED); assertEquals(actual, expected); } - @Test public void testPrepareFormFieldsIndexedSerialization() { String bodyText = "test for prepare form fields"; @@ -685,8 +663,8 @@ public void testPrepareFormFieldsIndexedSerialization() { List> expected = new ArrayList>(); expected.add(new SimpleEntry("body[0]", bodyText)); - List> actual = - CoreHelper.prepareFormFields(formParameters, ArraySerializationFormat.INDEXED); + List> actual = CoreHelper.prepareFormFields(formParameters, + ArraySerializationFormat.INDEXED); assertEquals(actual, expected); } @@ -698,8 +676,8 @@ public void testPrepareFormFieldsIndexedSerialization1() { formParameters.put("body", body); List> expected = new ArrayList>(); - List> actual = - CoreHelper.prepareFormFields(formParameters, ArraySerializationFormat.INDEXED); + List> actual = CoreHelper.prepareFormFields(formParameters, + ArraySerializationFormat.INDEXED); assertEquals(actual, expected); } @@ -712,8 +690,8 @@ public void testPrepareFormFieldsPlainSerialization() { List> expected = new ArrayList>(); expected.add(new SimpleEntry("body", bodyText)); - List> actual = - CoreHelper.prepareFormFields(formParameters, ArraySerializationFormat.PLAIN); + List> actual = CoreHelper.prepareFormFields(formParameters, + ArraySerializationFormat.PLAIN); assertEquals(actual, expected); } @@ -726,8 +704,8 @@ public void testPrepareFormFieldsUnindexedSerialization() { List> expected = new ArrayList>(); expected.add(new SimpleEntry("body[]", bodyText)); - List> actual = - CoreHelper.prepareFormFields(formParameters, ArraySerializationFormat.UNINDEXED); + List> actual = CoreHelper.prepareFormFields(formParameters, + ArraySerializationFormat.UNINDEXED); assertEquals(actual, expected); } @@ -741,8 +719,8 @@ public void testPrepareFormFieldsCSVSerialization() { List> expected = new ArrayList>(); expected.add(new SimpleEntry("body[0]", bodyText)); - List> actual = - CoreHelper.prepareFormFields(formParameters, ArraySerializationFormat.CSV); + List> actual = CoreHelper.prepareFormFields(formParameters, + ArraySerializationFormat.CSV); assertEquals(actual, expected); } @@ -755,8 +733,8 @@ public void testPrepareFormFieldPSVSerialization() { List> expected = new ArrayList>(); expected.add(new SimpleEntry("body[0]", bodyText)); - List> actual = - CoreHelper.prepareFormFields(formParameters, ArraySerializationFormat.PSV); + List> actual = CoreHelper.prepareFormFields(formParameters, + ArraySerializationFormat.PSV); assertEquals(actual, expected); } @@ -769,19 +747,18 @@ public void testPrepareFormFieldTSVSerialization() { List> expected = new ArrayList>(); expected.add(new SimpleEntry("body[0]", bodyText)); - List> actual = - CoreHelper.prepareFormFields(formParameters, ArraySerializationFormat.TSV); + List> actual = CoreHelper.prepareFormFields(formParameters, + ArraySerializationFormat.TSV); assertEquals(actual, expected); } - @Test public void testPrepareFormFields1() { Map formParameters = null; List> expected = new ArrayList>(); - List> actual = - CoreHelper.prepareFormFields(formParameters, ArraySerializationFormat.INDEXED); + List> actual = CoreHelper.prepareFormFields(formParameters, + ArraySerializationFormat.INDEXED); assertEquals(actual, expected); } @@ -804,9 +781,8 @@ public void testSerializeMapOfString() throws JsonProcessingException { @Test public void testUnixTimeStampSerializer() throws JsonProcessingException { - LocalDateTime localDateTime = - TestDateTimeHelper.getLocalDateTimeFromGMT(ZonedDateTime.of(YEAR1, MONTH1, DAY1, - HOUR1, MINUTES, 0, 0, ZoneId.of("GMT"))); + LocalDateTime localDateTime = TestDateTimeHelper.getLocalDateTimeFromGMT( + ZonedDateTime.of(YEAR1, MONTH1, DAY1, HOUR1, MINUTES, 0, 0, ZoneId.of("GMT"))); JsonSerializer serializer = new LocalDateTimeHelper.UnixTimestampSerializer(); String expected = "868756200"; String actual = CoreHelper.serialize(localDateTime, serializer); @@ -849,8 +825,8 @@ public void testUnixTimeStampSerializerNullObject() throws JsonProcessingExcepti @Test public void testUnixTimeStampSerializerNull() throws JsonProcessingException { - LocalDateTime localDateTime = - LocalDateTime.of(YEAR1, MONTH1, DAY1, XML_NO_OF_ELEMENT, MINUTES); + LocalDateTime localDateTime = LocalDateTime.of(YEAR1, MONTH1, DAY1, XML_NO_OF_ELEMENT, + MINUTES); JsonSerializer serializer = null; String actual = CoreHelper.serialize(localDateTime, serializer); @@ -862,10 +838,9 @@ public void testSimpleDateDeserializer() throws IOException { List expectedDates = new ArrayList<>(); expectedDates.add(LocalDate.of(YEAR2, MONTH2, DAY1)); expectedDates.add(LocalDate.of(YEAR2, MONTH2, DAY1)); - List actualDates = - CoreHelper.deserialize("[\"1994-02-13\",\"1994-02-13\"]", - new TypeReference>() {}, LocalDate.class, - new DateHelper.SimpleDateDeserializer()); + List actualDates = CoreHelper.deserialize("[\"1994-02-13\",\"1994-02-13\"]", + new TypeReference>() { + }, LocalDate.class, new DateHelper.SimpleDateDeserializer()); assertEquals(actualDates, expectedDates); } @@ -886,9 +861,9 @@ public void testDeserializerArrayNull() throws IOException { @Test public void testSimpleDateDeserializerNull() throws IOException { String json = null; - List actualDates = - CoreHelper.deserialize(json, new TypeReference>() {}, - LocalDate.class, new DateHelper.SimpleDateDeserializer()); + List actualDates = CoreHelper.deserialize(json, + new TypeReference>() { + }, LocalDate.class, new DateHelper.SimpleDateDeserializer()); assertNull(actualDates); } @@ -896,8 +871,8 @@ public void testSimpleDateDeserializerNull() throws IOException { public void testDeserializerWithClass() throws IOException { DeleteBody expected = new DeleteBody.Builder("ali", "QA").build(); - DeleteBody actual = - CoreHelper.deserialize("{\"name\": \"ali\", \"field\": \"QA\"}", DeleteBody.class); + DeleteBody actual = CoreHelper.deserialize("{\"name\": \"ali\", \"field\": \"QA\"}", + DeleteBody.class); assertEquals(actual.getName(), expected.getName()); assertEquals(actual.getField(), expected.getField()); } @@ -909,8 +884,6 @@ public void testDeserializerWithClass1() throws IOException { assertNull(actual); } - - @Test public void testDeserializeAsObject() { String json = "{\"name\": \"ali\", \"field\": \"QA\"}"; @@ -951,14 +924,16 @@ public void testDeserialize1() throws IOException { LinkedHashMap expected = new LinkedHashMap<>(); expected.put("name", "ali"); expected.put("field", "QA"); - Object actual = CoreHelper.deserialize(json, new TypeReference() {}); + Object actual = CoreHelper.deserialize(json, new TypeReference() { + }); assertEquals(actual, expected); } @Test public void testDeserialize2() throws IOException { String json = null; - Object actual = CoreHelper.deserialize(json, new TypeReference() {}); + Object actual = CoreHelper.deserialize(json, new TypeReference() { + }); assertNull(actual); } @@ -972,25 +947,22 @@ public void testDeserializeNullJson() throws IOException { @Test public void testDeserializeOneOfNull() throws IOException { JsonNode jsonNode = null; - Object result = - CoreHelper.deserialize(jsonNode, Arrays.asList(AtomCase.class, OrbitCase.class), - true); + Object result = CoreHelper.deserialize(jsonNode, + Arrays.asList(AtomCase.class, OrbitCase.class), true); assertNull(result); } @Test public void testDeserializeOneOfA() throws IOException { - String json = - "{\"key1\":{\"NumberOfElectrons\":2,\"NumberOfProtons\":2}," - + "\"key2\":{\"NumberOfElectrons\":2,\"NumberOfProtons\":2}}"; + String json = "{\"key1\":{\"NumberOfElectrons\":2,\"NumberOfProtons\":2}," + + "\"key2\":{\"NumberOfElectrons\":2,\"NumberOfProtons\":2}}"; ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonNode = objectMapper.readTree(json); - Object result = - CoreHelper.deserialize(jsonNode, Arrays.asList(AtomCase.class, OrbitCase.class), - true); + Object result = CoreHelper.deserialize(jsonNode, + Arrays.asList(AtomCase.class, OrbitCase.class), true); assertEquals("{key1=Atom [numberOfElectrons=2, numberOfProtons=2], " - + "key2=Atom [numberOfElectrons=2, numberOfProtons=2]}", result.toString()); + + "key2=Atom [numberOfElectrons=2, numberOfProtons=2]}", result.toString()); } @Test @@ -999,9 +971,8 @@ public void testDeserializeOneOfB() throws IOException { ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonNode = objectMapper.readTree(json); - Object result = - CoreHelper.deserialize(jsonNode, Arrays.asList(AtomCase.class, OrbitCase.class), - true); + Object result = CoreHelper.deserialize(jsonNode, + Arrays.asList(AtomCase.class, OrbitCase.class), true); assertEquals("Orbit [numberOfElectrons=2]", result.toString()); } @@ -1011,12 +982,12 @@ public void testDeserializeOneOfFailA() throws IOException { ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonNode = objectMapper.readTree(json); try { - CoreHelper.deserialize(jsonNode, Arrays.asList(AtomCase.class, OrbitCase.class), true); + CoreHelper.deserialize(jsonNode, Arrays.asList(AtomCase.class, OrbitCase.class), true); } catch (Exception e) { - assertEquals("We could not match any acceptable type from " - + "Map, Orbit on: {\"RandomKey\":2}", e.getMessage()); - throw e; - } + assertEquals("We could not match any acceptable type from " + + "Map, Orbit on: {\"RandomKey\":2}", e.getMessage()); + throw e; + } } @Test(expected = OneOfValidationException.class) @@ -1025,27 +996,25 @@ public void testDeserializeOneOfFailB() throws IOException { ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonNode = objectMapper.readTree(json); try { - CoreHelper.deserialize(jsonNode, Arrays.asList(OrbitCase.class, OrbitCase.class), true); + CoreHelper.deserialize(jsonNode, Arrays.asList(OrbitCase.class, OrbitCase.class), true); } catch (Exception e) { - assertEquals("There are more than one matching types i.e. Orbit and " - + "Orbit on: {\"NumberOfElectrons\":2}", e.getMessage()); - throw e; - } + assertEquals("There are more than one matching types i.e. Orbit and " + + "Orbit on: {\"NumberOfElectrons\":2}", e.getMessage()); + throw e; + } } @Test public void testDeserializeAnyOfA() throws IOException { - String json = - "{\"key1\":{\"NumberOfElectrons\":2,\"NumberOfProtons\":2}," - + "\"key2\":{\"NumberOfElectrons\":2,\"NumberOfProtons\":2}}"; + String json = "{\"key1\":{\"NumberOfElectrons\":2,\"NumberOfProtons\":2}," + + "\"key2\":{\"NumberOfElectrons\":2,\"NumberOfProtons\":2}}"; ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonNode = objectMapper.readTree(json); - Object result = - CoreHelper.deserialize(jsonNode, Arrays.asList(AtomCase.class, OrbitCase.class), - false); + Object result = CoreHelper.deserialize(jsonNode, + Arrays.asList(AtomCase.class, OrbitCase.class), false); assertEquals("{key1=Atom [numberOfElectrons=2, numberOfProtons=2], " - + "key2=Atom [numberOfElectrons=2, numberOfProtons=2]}", result.toString()); + + "key2=Atom [numberOfElectrons=2, numberOfProtons=2]}", result.toString()); } @Test @@ -1054,9 +1023,8 @@ public void testDeserializeAnyOfB() throws IOException { ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonNode = objectMapper.readTree(json); - Object result = - CoreHelper.deserialize(jsonNode, Arrays.asList(AtomCase.class, OrbitCase.class), - false); + Object result = CoreHelper.deserialize(jsonNode, + Arrays.asList(AtomCase.class, OrbitCase.class), false); assertEquals("Orbit [numberOfElectrons=2]", result.toString()); } @@ -1066,28 +1034,27 @@ public void testDeserializeAnyOfFailA() throws IOException { ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonNode = objectMapper.readTree(json); try { - CoreHelper.deserialize(jsonNode, Arrays.asList(AtomCase.class, OrbitCase.class), false); + CoreHelper.deserialize(jsonNode, Arrays.asList(AtomCase.class, OrbitCase.class), false); } catch (Exception e) { - assertEquals("We could not match any acceptable type from " - + "Map, Orbit on: {\"RandomKey\":2}", e.getMessage()); - throw e; - } + assertEquals("We could not match any acceptable type from " + + "Map, Orbit on: {\"RandomKey\":2}", e.getMessage()); + throw e; + } } @Test(expected = AnyOfValidationException.class) public void testDeserializeAnyOfFailB() throws IOException { - String json = - "[{\"NumberOfElectrons\":2},{\"NumberOfElectrons\":2, \"NumberOfProtons\":2}]"; + String json = "[{\"NumberOfElectrons\":2},{\"NumberOfElectrons\":2, \"NumberOfProtons\":2}]"; ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonNode = objectMapper.readTree(json); try { - CoreHelper.deserialize(jsonNode, Arrays.asList(AtomCase.class, OrbitCase.class), false); + CoreHelper.deserialize(jsonNode, Arrays.asList(AtomCase.class, OrbitCase.class), false); } catch (Exception e) { - assertEquals("We could not match any acceptable type " - + "from Map, Orbit on: [{\"NumberOfElectrons\":2}," - + "{\"NumberOfElectrons\":2,\"NumberOfProtons\":2}]", e.getMessage()); - throw e; - } + assertEquals("We could not match any acceptable type " + + "from Map, Orbit on: [{\"NumberOfElectrons\":2}," + + "{\"NumberOfElectrons\":2,\"NumberOfProtons\":2}]", e.getMessage()); + throw e; + } } @Test @@ -1113,15 +1080,15 @@ public void testTypeCombinatorSerializationString1() throws JsonProcessingExcept Map mapOfTypes = new HashMap<>(); mapOfTypes.put("key1", body); - List> actual = - CoreHelper.prepareFormFields(mapOfTypes, ArraySerializationFormat.INDEXED); + List> actual = CoreHelper.prepareFormFields(mapOfTypes, + ArraySerializationFormat.INDEXED); assertEquals(actual, expected); } @Test public void testTypeCombinatorSerializationInteger() throws JsonProcessingException { - SendScalarParamBody body = - SendScalarParamBody.fromPrecision(Arrays.asList(PRECISION_NUMBER)); + SendScalarParamBody body = SendScalarParamBody + .fromPrecision(Arrays.asList(PRECISION_NUMBER)); String expected = "[1.2]"; String actual = CoreHelper.serializeTypeCombinator(body); assertEquals(actual, expected); @@ -1133,7 +1100,8 @@ public void testDeserializeTypeReferenceJsonNode() throws IOException { ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonNode = objectMapper.readTree(json); - Object result = CoreHelper.deserialize(jsonNode, new TypeReference() {}); + Object result = CoreHelper.deserialize(jsonNode, new TypeReference() { + }); assertNotNull(result); } @@ -1141,7 +1109,8 @@ public void testDeserializeTypeReferenceJsonNode() throws IOException { public void testDeserializeTypeReferenceJsonNode1() throws IOException { JsonNode jsonNode = null; - Object result = CoreHelper.deserialize(jsonNode, new TypeReference() {}); + Object result = CoreHelper.deserialize(jsonNode, new TypeReference() { + }); assertNull(result); } @@ -1152,10 +1121,9 @@ public void testDeserializeThroughParser() throws IOException { DeserializationContext context = mapper.getDeserializationContext(); JsonParser jsonParser = mapper.createParser("{\"NumberOfTyres\":\"4\",\"HaveTrunk\":true}"); - Object actual = - CoreHelper.deserialize(jsonParser, context, discriminator, - Arrays.asList(Collections.singletonMap("Morning", MorningCase.class)), - Arrays.asList(CarCase.class, AtomCase.class), true); + Object actual = CoreHelper.deserialize(jsonParser, context, discriminator, + Arrays.asList(Collections.singletonMap("Morning", MorningCase.class)), + Arrays.asList(CarCase.class, AtomCase.class), true); assertNotNull(actual); } @@ -1172,7 +1140,6 @@ public void testDeserializeThroughParser1() throws IOException { Arrays.asList(), true); } - @Test public void testDeserializeThroughParser2() throws IOException { String discriminator = ""; @@ -1190,8 +1157,8 @@ public void testDeserializeThroughParser3() throws IOException { String discriminator = ""; ObjectMapper mapper = new ObjectMapper(); DeserializationContext context = mapper.getDeserializationContext(); - JsonParser jsonParser = - mapper.createParser("[{\"NumberOfTyres\":\"4\",\"HaveTrunk\":true}]"); + JsonParser jsonParser = mapper + .createParser("[{\"NumberOfTyres\":\"4\",\"HaveTrunk\":true}]"); CoreHelper.deserialize(jsonParser, context, discriminator, Arrays.asList(Collections.singletonMap("Morning", MorningCase.class)), @@ -1203,8 +1170,8 @@ public void testDeserializeThroughParser4() throws IOException { String discriminator = ""; ObjectMapper mapper = new ObjectMapper(); DeserializationContext context = mapper.getDeserializationContext(); - JsonParser jsonParser = - mapper.createParser("[{\"NumberOfTyres\":\"4\",\"HaveTrunk\":true}]"); + JsonParser jsonParser = mapper + .createParser("[{\"NumberOfTyres\":\"4\",\"HaveTrunk\":true}]"); CoreHelper.deserialize(jsonParser, context, discriminator, Arrays.asList(Collections.singletonMap("Morning", MorningCase.class)), null, true); @@ -1219,37 +1186,35 @@ public void testDeserializeThroughParser5() throws IOException { @Test public void testPrepareFormFieldOneOfAnyOf() throws IOException { - NonScalarModel formNonScalarModel = - CoreHelper.deserialize( - "{\"outerMap" + "\":{\"key1\":{\"startsAt\":\"15:00\",\"endsAt\":" - + "\"21:00\",\"offerLunch\":true,\"sessionType\":\"Noon\"}" - + ",\"key2\":{\"startsAt\":\"6:00\",\"endsAt\":\"11:00\"," - + "\"offerTeaBreak\":true,\"sessionType\":\"Morning\"}}}", - NonScalarModel.class); + NonScalarModel formNonScalarModel = CoreHelper.deserialize( + "{\"outerMap" + "\":{\"key1\":{\"startsAt\":\"15:00\",\"endsAt\":" + + "\"21:00\",\"offerLunch\":true,\"sessionType\":\"Noon\"}" + + ",\"key2\":{\"startsAt\":\"6:00\",\"endsAt\":\"11:00\"," + + "\"offerTeaBreak\":true,\"sessionType\":\"Morning\"}}}", + NonScalarModel.class); Map formParameters = new HashMap<>(); formParameters.put("Key1", formNonScalarModel); - List> actual = - CoreHelper.prepareFormFields(formParameters, ArraySerializationFormat.INDEXED); + List> actual = CoreHelper.prepareFormFields(formParameters, + ArraySerializationFormat.INDEXED); assertNotNull(actual); } @Test public void testPrepareFormFieldOneOfAnyOfDateTime() throws IOException { - DateTimeCases formDateTimeCases = - CoreHelper.deserialize( + DateTimeCases formDateTimeCases = CoreHelper + .deserialize( "{\"mapvsArray\":{\"key1\":" + "\"Sun, 06 Nov 1994 08:49:37 GMT\"," + "\"key2\":\"Sun, 06 Nov 1994 08:49:37 GMT\"}}", DateTimeCases.class); Map formParameters = new HashMap<>(); formParameters.put("DateTime", formDateTimeCases); - List> actual = - CoreHelper.prepareFormFields(formParameters, ArraySerializationFormat.INDEXED); + List> actual = CoreHelper.prepareFormFields(formParameters, + ArraySerializationFormat.INDEXED); assertNotNull(actual); } - @Test public void testDeserializeArrayJsonNode() throws IOException { String json = "[1.6, 2.3]"; @@ -1260,7 +1225,6 @@ public void testDeserializeArrayJsonNode() throws IOException { assertEquals(actualArray, expectedArray); } - @Test public void testDeserializeArrayJsonNode1() throws IOException { JsonNode jsonNode = null; @@ -1296,8 +1260,8 @@ public void testFormSerializationAnnotation() throws IOException { Map formParameters = new HashMap<>(); formParameters.put("date", formDateTime); - List> formFields = - CoreHelper.prepareFormFields(formParameters, ArraySerializationFormat.PLAIN); + List> formFields = CoreHelper.prepareFormFields(formParameters, + ArraySerializationFormat.PLAIN); assertNotNull(formFields); } @@ -1313,44 +1277,40 @@ public void testFormSerializationAnnotation1() throws IOException { Map formParameters = new HashMap<>(); formParameters.put("date", formDateTime); - List> formFields = - CoreHelper.prepareFormFields(formParameters, ArraySerializationFormat.PLAIN); + List> formFields = CoreHelper.prepareFormFields(formParameters, + ArraySerializationFormat.PLAIN); assertNotNull(formFields); } @Test public void testSerializeXMLArray() throws IOException { String expected = XML_ARRAY.replace("\r\n", ""); - AttributesAndElements elements = - new AttributesAndElements.Builder("XMLRootName", XML_NO_OF_ATTRIBUTE, "Data", - XML_NO_OF_ELEMENT).build(); + AttributesAndElements elements = new AttributesAndElements.Builder("XMLRootName", + XML_NO_OF_ATTRIBUTE, "Data", XML_NO_OF_ELEMENT).build(); List attributesAndElements = new ArrayList<>(); attributesAndElements.add(elements); - String actual = - CoreHelper.serializeXmlArray( - attributesAndElements - .toArray(new AttributesAndElements[attributesAndElements.size()]), - "arrayOfModels", "item", AttributesAndElements.class); + String actual = CoreHelper.serializeXmlArray( + attributesAndElements + .toArray(new AttributesAndElements[attributesAndElements.size()]), + "arrayOfModels", "item", AttributesAndElements.class); assertEquals(actual.replace("\n", ""), expected); } @Test public void testSerializeXML() throws IOException { String expected = XML.replace("\r\n", ""); - AttributesAndElements elements = - new AttributesAndElements.Builder("XMLRootName", XML_NO_OF_ATTRIBUTE, "Data", - XML_NO_OF_ELEMENT).build(); + AttributesAndElements elements = new AttributesAndElements.Builder("XMLRootName", + XML_NO_OF_ATTRIBUTE, "Data", XML_NO_OF_ELEMENT).build(); - String actual = - CoreHelper.serializeXml(elements, "arrayOfModels", AttributesAndElements.class); + String actual = CoreHelper.serializeXml(elements, "arrayOfModels", + AttributesAndElements.class); assertEquals(actual.replace("\n", ""), expected); } @Test public void testDeserializeXML() throws IOException { - AttributesAndElements expected = - new AttributesAndElements.Builder("XMLRootName", XML_NO_OF_ATTRIBUTE, "Data", - XML_NO_OF_ELEMENT).build(); + AttributesAndElements expected = new AttributesAndElements.Builder("XMLRootName", + XML_NO_OF_ATTRIBUTE, "Data", XML_NO_OF_ELEMENT).build(); AttributesAndElements actual = CoreHelper.deserializeXml(XML, AttributesAndElements.class); assertEquals(actual.getNumberAttr(), expected.getNumberAttr()); @@ -1361,14 +1321,13 @@ public void testDeserializeXML() throws IOException { @Test public void testDeserializeXMLArray() throws IOException { - AttributesAndElements elements = - new AttributesAndElements.Builder("XMLRootName", XML_NO_OF_ATTRIBUTE, "Data", - XML_NO_OF_ELEMENT).build(); + AttributesAndElements elements = new AttributesAndElements.Builder("XMLRootName", + XML_NO_OF_ATTRIBUTE, "Data", XML_NO_OF_ELEMENT).build(); List expected = new ArrayList<>(); expected.add(elements); - List actual = - CoreHelper.deserializeXmlArray(XML_ARRAY, AttributesAndElements[].class); + List actual = CoreHelper.deserializeXmlArray(XML_ARRAY, + AttributesAndElements[].class); assertEquals(actual.get(0).getNumberAttr(), expected.get(0).getNumberAttr()); } @@ -1391,16 +1350,15 @@ public void testDeserializeXMLArrayTypes() throws IOException { @Test public void testJSonObjectDeserialization() throws IOException { - CoreJsonObject body = - CoreJsonObject.fromJsonString( - "{\"$id\":\"https://example.com/person.schema.json\",\"$schema\":" - + "\"https://json-schema.org/draft/2020-12/schema\",\"title\":" - + "\"Person\",\"type\":\"object\",\"properties\":" - + "{\"firstName\":{\"type\":\"string\",\"description\":" - + "\"The person's first name.\"},\"lastName\":" - + "{\"type\":\"string\",\"description\":\"The person's last n" - + "ame.\",\"test\":null},\"age\":{\"type\":\"integer\"," - + "\"description\":\"Age in years\",\"minimum\":0}}}"); + CoreJsonObject body = CoreJsonObject + .fromJsonString("{\"$id\":\"https://example.com/person.schema.json\",\"$schema\":" + + "\"https://json-schema.org/draft/2020-12/schema\",\"title\":" + + "\"Person\",\"type\":\"object\",\"properties\":" + + "{\"firstName\":{\"type\":\"string\",\"description\":" + + "\"The person's first name.\"},\"lastName\":" + + "{\"type\":\"string\",\"description\":\"The person's last n" + + "ame.\",\"test\":null},\"age\":{\"type\":\"integer\"," + + "\"description\":\"Age in years\",\"minimum\":0}}}"); String baseUri = "https://localhost:3000"; StringBuilder queryBuilder = new StringBuilder(baseUri + "/query"); @@ -1431,52 +1389,51 @@ public void testJSonValueDeserialization() throws IOException { } private Map getComplexType() throws IOException { - Map complexType = - CoreHelper.deserialize( - "{\"key1\": {\"numberListType\":[555,666,777],\"numberMapType\":" - + "{\"num1\":1,\"num3\":2,\"num2\":3},\"innerComplexType" - + "\":{\"stringType\":\"MyString1\",\"booleanTyp" - + "e\":true,\"dateTimeType\":\"1994-11-06T08:49:37Z\"" - + ",\"dateType\":\"1994-02-13\",\"uuidType\":" - + "\"a5e48529-745b-4dfb-aac0-a7d844debd8b\"," - + "\"longType\":500000000,\"precisionType\":5.43," - + "\"objectType\":{\"long2\":1000000000,\"long1\":500000000}," - + "\"stringListType\":[\"Item1\",\"Item2\"]}," - + "\"innerComplexListType\":[{\"stringTyp" - + "e\":\"MyString1\",\"booleanType\":true,\"dateTimeType\":" - + "\"1994-11-06T08:49:37Z\",\"dateType\":\"1994-02-13\"," - + "\"uuidType\":\"a5e48529-745b-4dfb-aac0-a7d844debd" - + "8b\",\"longType\":500000000,\"precisionType\":5.43," - + "\"objectType\":{\"long2\":1000000000,\"long1\":500000000}" - + ",\"stringListType\":[\"Item1\",\"Item2\"]},{\"string" - + "Type\":\"MyString2\",\"booleanType\":false," - + "\"dateTimeType\":\"1994-11-07T08:49:37Z\",\"dateType\":" - + "\"1994-02-12\",\"uuidType\":\"b46ba2d3-b4ac-4b40-ae62-6326e88c" - + "89a6\",\"longType\":1000000000,\"precisionType\":5.43," - + "\"objectType\":{\"bool1\":true,\"bool2\":false}," - + "\"stringListType\":[\"Item1\",\"Item2\"]}]}, \"key2\": {" - + "\"numberListType\":[555,666,777],\"numberMapType\":" - + "{\"num1\":1,\"num3\":2,\"num2\":3},\"innerComplexType\":" - + "{\"stringType\":\"MyString1\",\"booleanType\":true," - + "\"dateTimeType\":\"1994-11-06T08:49:37Z\",\"dateType\":" - + "\"1994-02-13\",\"uuidType\":\"a5e48529-745b-4dfb-aac0-" - + "a7d844debd8b\",\"longType\":500000000,\"precisionType\":" - + "5.43,\"objectType\":{\"long2\":1000000000,\"long1\":500000000}" - + ",\"stringListType\":[\"Item1\",\"Item2\"]}," - + "\"innerComplexListType\":[{\"stringType\":\"MyString1\"," - + "\"booleanType\":true,\"dateTimeType\":\"1994-11-06T08" - + ":49:37Z\",\"dateType\":\"1994-02-13\",\"uuidType\":" - + "\"a5e48529-745b-4dfb-aac0-a7d844debd8b\",\"longTy" - + "pe\":500000000,\"precisionType\":5.43,\"objectType\"" - + ":{\"long2\":1000000000,\"long1\":500000000},\"stringListType\"" - + ":[\"Item1\",\"Item2\"]},{\"stringType\":\"MySt" - + "ring2\",\"booleanType\":false,\"dateTimeType\":" - + "\"1994-11-07T08:49:37Z\",\"dateType\":\"1994-02-12\"," - + "\"uuidType\":\"b46ba2d3-b4ac-4b40-ae62-6326e88c89a6\",\"long" - + "Type\":1000000000,\"precisionType\":5.43,\"objectType\":" - + "{\"bool1\":true,\"bool2\":false},\"stringListType\":[\"Item1\"," - + "\"Item2\"]}]}}", - new TypeReference>() {}); + Map complexType = CoreHelper + .deserialize("{\"key1\": {\"numberListType\":[555,666,777],\"numberMapType\":" + + "{\"num1\":1,\"num3\":2,\"num2\":3},\"innerComplexType" + + "\":{\"stringType\":\"MyString1\",\"booleanTyp" + + "e\":true,\"dateTimeType\":\"1994-11-06T08:49:37Z\"" + + ",\"dateType\":\"1994-02-13\",\"uuidType\":" + + "\"a5e48529-745b-4dfb-aac0-a7d844debd8b\"," + + "\"longType\":500000000,\"precisionType\":5.43," + + "\"objectType\":{\"long2\":1000000000,\"long1\":500000000}," + + "\"stringListType\":[\"Item1\",\"Item2\"]}," + + "\"innerComplexListType\":[{\"stringTyp" + + "e\":\"MyString1\",\"booleanType\":true,\"dateTimeType\":" + + "\"1994-11-06T08:49:37Z\",\"dateType\":\"1994-02-13\"," + + "\"uuidType\":\"a5e48529-745b-4dfb-aac0-a7d844debd" + + "8b\",\"longType\":500000000,\"precisionType\":5.43," + + "\"objectType\":{\"long2\":1000000000,\"long1\":500000000}" + + ",\"stringListType\":[\"Item1\",\"Item2\"]},{\"string" + + "Type\":\"MyString2\",\"booleanType\":false," + + "\"dateTimeType\":\"1994-11-07T08:49:37Z\",\"dateType\":" + + "\"1994-02-12\",\"uuidType\":\"b46ba2d3-b4ac-4b40-ae62-6326e88c" + + "89a6\",\"longType\":1000000000,\"precisionType\":5.43," + + "\"objectType\":{\"bool1\":true,\"bool2\":false}," + + "\"stringListType\":[\"Item1\",\"Item2\"]}]}, \"key2\": {" + + "\"numberListType\":[555,666,777],\"numberMapType\":" + + "{\"num1\":1,\"num3\":2,\"num2\":3},\"innerComplexType\":" + + "{\"stringType\":\"MyString1\",\"booleanType\":true," + + "\"dateTimeType\":\"1994-11-06T08:49:37Z\",\"dateType\":" + + "\"1994-02-13\",\"uuidType\":\"a5e48529-745b-4dfb-aac0-" + + "a7d844debd8b\",\"longType\":500000000,\"precisionType\":" + + "5.43,\"objectType\":{\"long2\":1000000000,\"long1\":500000000}" + + ",\"stringListType\":[\"Item1\",\"Item2\"]}," + + "\"innerComplexListType\":[{\"stringType\":\"MyString1\"," + + "\"booleanType\":true,\"dateTimeType\":\"1994-11-06T08" + + ":49:37Z\",\"dateType\":\"1994-02-13\",\"uuidType\":" + + "\"a5e48529-745b-4dfb-aac0-a7d844debd8b\",\"longTy" + + "pe\":500000000,\"precisionType\":5.43,\"objectType\"" + + ":{\"long2\":1000000000,\"long1\":500000000},\"stringListType\"" + + ":[\"Item1\",\"Item2\"]},{\"stringType\":\"MySt" + + "ring2\",\"booleanType\":false,\"dateTimeType\":" + + "\"1994-11-07T08:49:37Z\",\"dateType\":\"1994-02-12\"," + + "\"uuidType\":\"b46ba2d3-b4ac-4b40-ae62-6326e88c89a6\",\"long" + + "Type\":1000000000,\"precisionType\":5.43,\"objectType\":" + + "{\"bool1\":true,\"bool2\":false},\"stringListType\":[\"Item1\"," + + "\"Item2\"]}]}}", new TypeReference>() { + }); return complexType; } } From 5e8ae8c2243062702691c261d7d72468e0321efb Mon Sep 17 00:00:00 2001 From: Asad Ali Date: Wed, 24 May 2023 10:59:41 +0500 Subject: [PATCH 3/4] fixed spacing issues --- .../apimatic/core/utilities/CoreHelper.java | 94 +++++++++---------- .../core/utilities/CoreHelperTest.java | 12 ++- 2 files changed, 55 insertions(+), 51 deletions(-) diff --git a/src/main/java/io/apimatic/core/utilities/CoreHelper.java b/src/main/java/io/apimatic/core/utilities/CoreHelper.java index 17254e59..c7977aa8 100644 --- a/src/main/java/io/apimatic/core/utilities/CoreHelper.java +++ b/src/main/java/io/apimatic/core/utilities/CoreHelper.java @@ -116,7 +116,7 @@ protected CoreHelper() { /** * Get a JsonSerializer instance for a collection from the provided annotation. - * + * * @param serializerAnnotation The Annotation containing information about the * custom serializer of a collection. * @return The JsonSerializer instance of the required type. @@ -141,7 +141,7 @@ private static JsonSerializer getCollectionCustomSerializer( /** * Get a JsonSerializer instance from the provided annotation. - * + * * @param serializerAnnotation The Annotation containing information about the * serializer. * @return The JsonSerializer instance of the required type. @@ -156,7 +156,7 @@ private static JsonSerializer getSerializer(JsonSerialize serializerAnnotatio /** * Get a JsonSerializer instance for a collection from the provided annotation. - * + * * @param serializerAnnotation The Annotation containing information about the * serializer of a collection. * @return The JsonSerializer instance of the required type. @@ -171,7 +171,7 @@ private static JsonSerializer getCollectionSerializer(JsonSerialize serialize /** * Deserialization of Json data. - * + * * @return {@link ObjectMapper}. */ public static ObjectMapper getMapper() { @@ -189,7 +189,7 @@ public static ObjectMapper getStrictMapper() { /** * Json Serialization of a given object. - * + * * @param obj The object to serialize into Json. * @return The serialized Json String representation of the given object. * @throws JsonProcessingException Signals that a Json Processing Exception has @@ -205,7 +205,7 @@ public static String serialize(Object obj) throws JsonProcessingException { /** * Json Serialization of a given object using a specified JsonSerializer. - * + * * @param obj The object to serialize into Json. * @param serializer The instance of JsonSerializer to use. * @return The serialized Json string representation of the given object. @@ -239,7 +239,7 @@ public static String serialize(Object obj, final JsonSerializer serializer) /** * Xml Serialization of a given object list. - * + * * @param Type of object to be serialized. * @param objArray Object Array to be serialized. * @param rootName Root name for the xml. @@ -273,7 +273,7 @@ public static String serializeXmlArray(T[] objArray, String rootName, String /** * Xml Serialization of a given object. - * + * * @param Type of object to be serialized. * @param obj Object to be serialized. * @param rootName Root name for the xml. @@ -298,7 +298,7 @@ public static String serializeXml(T obj, String rootName, Class cls) thro /** * Json Serialization of a given container object based on annotation. - * + * * @param obj The object to serialize into Json. * @return The serialized Json String representation of the given object. * @throws JsonProcessingException Signals that a Json Processing Exception has @@ -322,7 +322,7 @@ public static String serializeTypeCombinator(Object obj) throws JsonProcessingEx /** * Json deserialization of the given Json string using a specified * JsonDerializer. - * + * * @param jsonNode The JsonNode to deserialize. * @param typeReference TypeReference of T1. * @param The type of the object to deserialize into. @@ -345,7 +345,7 @@ public static T1 deserialize(JsonNode jso /** * Json deserialization of the given Json string using a specified * JsonDerializer. - * + * * @param json The Json string to deserialize. * @param typeReference TypeReference of T1. * @param The type of the object to deserialize into. @@ -374,7 +374,7 @@ public static T1 deserialize(String json, /** * Json deserialization of the given Json string. - * + * * @param The type of the object to deserialize into. * @param json The Json string to deserialize. * @param clazz The type of the object to deserialize into. @@ -393,7 +393,7 @@ public static T deserialize(String json, Class clazz) thro * Strict JSON deserialization of the given JSON string with * FAIL_ON_UNKNOWN_PROPERTIES flag as true, used particularly for type * combinators. - * + * * @param The type of the object to deserialize into * @param json The JsonNode to deserialize * @param classes The list of types of the object to deserialize into @@ -446,7 +446,7 @@ private static String getTypeCombinatorCaseType(Class clazz) { /** * Json deserialization of the given Json string. - * + * * @param json The Json string to deserialize. * @return The deserialized Json as a Map. * @throws IOException Signals if any I/O exception occurred. @@ -456,14 +456,14 @@ public static LinkedHashMap deserialize(String json) throws IOEx return null; } - TypeReference> typeRef = new TypeReference>() { - }; + TypeReference> typeRef = + new TypeReference>() {}; return deserialize(json, typeRef); } /** * JSON Deserialization of the given json string. - * + * * @param json The json string to deserialize. * @param typeReference TypeReference of T. * @param The type of the object to deserialize into. @@ -482,7 +482,7 @@ public static T deserialize(String json, TypeReference typ /** * JSON Deserialization of the given json string with FAIL_ON_UNKNOWN_PROPERTIES * flag as true. - * + * * @param jsonNode The Json Node to deserialize. * @param typeReference TypeReference of T. * @param The type of the object to deserialize into. @@ -501,7 +501,7 @@ public static T deserialize(JsonNode jsonNode, /** * JSON deserialization of the given JsonNode with FAIL_ON_UNKNOWN_PROPERTIES * flag as true. - * + * * @param The type of the object to deserialize into. * @param jsonNode The Json Node to deserialize. * @param clazz The type of the object to deserialize into. @@ -518,7 +518,7 @@ public static T deserialize(JsonNode jsonNode, Class clazz /** * XML Deserialization of the given xml string. - * + * * @param The class of the object to deserialize into. * @param xml The xml string to deserialize. * @param cls The class of the object to deserialize into. @@ -540,7 +540,7 @@ public static T deserializeXml(String xml, Class cls) thro /** * XML Deserialization of the given xml string. - * + * * @param The class of the object to deserialize into. * @param xml The xml string to deserialize. * @param cls The class of the object to deserialize into. @@ -564,7 +564,7 @@ public static List deserializeXmlArray(String xml, Class The class of the object to deserialize into. * @param xml The xml string to deserialize. * @param cls The class of the object to deserialize into. @@ -594,7 +594,7 @@ public static List deserializeXmlSimpleTypesArray(String x /** * JSON Deserialization from custom deserializer based on given discriminator * and registry. - * + * * @param jp Parsed used for reading JSON content. * @param ctxt Context that can be used to access * information about this deserialization @@ -630,7 +630,7 @@ public static T deserialize(JsonParser jp, DeserializationContext ctxt, /** * Json deserialization of the given Json string. - * + * * @param json The Json string to deserialize. * @return The deserialized Json as an Object. */ @@ -650,7 +650,7 @@ public static Object deserializeAsObject(String json) { /** * JSON Deserialization of the given json string. - * + * * @param The type of the object to deserialize into. * @param json The Json string to deserialize. * @param classArray The class of the array of objects to deserialize into. @@ -668,7 +668,7 @@ public static List deserializeArray(String json, Class us /** * Removes null values from the given map. - * + * * @param map Map of values. */ public static void removeNullValues(Map map) { @@ -819,7 +819,7 @@ public static void removeNullValues(Map map) { /** * Validates and processes the given URL. - * + * * @param url The given URL to process. * @return Pre-process URL as string. */ @@ -844,7 +844,7 @@ public static String cleanUrl(StringBuilder url) { /** * Prepares Array style form fields from a given array of values. - * + * * @param value Value for the form fields. * @param arraySerializationFormat serialization format. * @return Dictionary of form fields created from array elements. @@ -861,7 +861,7 @@ public static List> prepareFormFields(Map valu /** * JSON Deserialization of the given json string with FAIL_ON_UNKNOWN_PROPERTIES * flag as true. - * + * * @param The type of the object to deserialize into. * @param json The Json string to deserialize. * @param classArray The class of the array of objects to deserialize into. @@ -879,7 +879,7 @@ public static List deserializeArray(JsonNode json, Class List> deduceType(JsonNode jsonNode, String /** * Deduces the type from immediate child if exists based on given discriminator. - * + * * @param jsonNode The json to check against. * @param discriminator The model's discriminator. * @return The type to deserialize into. @@ -945,7 +945,7 @@ private static String deduceTypeFromImmidiateChild(JsonNode jsonNode, String dis /** * Encodes a given object to URL encoded string. - * + * * @param name Name of the object. * @param obj Raw object sent from caller. * @param objBuilder String of elements. @@ -1007,7 +1007,7 @@ private static void appendParamKeyValuePair(String formatString, StringBuilder o /** * Flattening a collection of objects into a string. - * + * * @param elemName The element name of collection. * @param array Array of elements to flatten. * @param encode Need to encode?. @@ -1045,7 +1045,7 @@ private static String flattenCollection(String elemName, Collection array, bo /** * Tries URL encode using UTF-8. - * + * * @param value The value to URL encode. * @param spaceAsPercentEncoded The flag get space character as percent encoded. * @return Encoded url. @@ -1064,7 +1064,7 @@ public static String tryUrlEncode(String value, boolean spaceAsPercentEncoded) { /** * Responsible to encode into base64 the username and password - * + * * @param basicAuthUserName The auth username * @param basicAuthPassword The auth password * @return The base64 encoded String @@ -1119,7 +1119,7 @@ private static void objectToList(String objName, Map obj, /** * Converts a given object to a form encoded map. - * + * * @param objName Name of the object. * @param obj The object to convert into a map. * @param objectList The object list to populate. @@ -1275,7 +1275,7 @@ private static boolean isDelimeterFormat(ArraySerializationFormat arraySerializa /** * Processes the value and load into objectList against key. - * + * * @param key The key to used for creation of key value * pair. * @param value The value to process against the given key. @@ -1306,7 +1306,7 @@ private static void loadKeyValueUsingSerializer(String key, Object value, /** * While processing objects to map, loads value after serializing. - * + * * @param key The key to used for creation of key value * pair. * @param value The value to process against the given key. @@ -1336,7 +1336,7 @@ private static void loadKeyValuePairForEncoding(String key, Object value, /** * While processing objects to map, decides whether to perform recursion or load * value. - * + * * @param key The key for creating key value pair. * @param value The value to process against the given key. * @param objectList The object list to process with key value @@ -1372,7 +1372,7 @@ private static void loadKeyValuePairForEncoding(String key, Object value, /** * While processing objects to map, loads value after serializing. - * + * * @param key The key to used for creation of key value * pair. * @param value The value to process against the given key. @@ -1404,7 +1404,7 @@ private static void loadKeyValuePairForEncoding(String key, Object value, /** * Check if the given object can be wrapped directly. - * + * * @param object The given object. * @return true if the class is an autoboxed class e.g., Integer. */ @@ -1418,7 +1418,7 @@ private static boolean isWrapperType(Object object) { /** * Json Serialization of an ENUM defined under oneOf/anyOf container. - * + * * @param value The object to serialize into Json String. * @return The serialized Json String representation of the given object. * @throws JsonProcessingException Signals that a Json Processing Exception has diff --git a/src/test/java/apimatic/core/utilities/CoreHelperTest.java b/src/test/java/apimatic/core/utilities/CoreHelperTest.java index 05e6af31..6abdf7b8 100644 --- a/src/test/java/apimatic/core/utilities/CoreHelperTest.java +++ b/src/test/java/apimatic/core/utilities/CoreHelperTest.java @@ -78,12 +78,14 @@ public class CoreHelperTest { private static final String INVALID_XML = "\r\n" + " item number=\"3\" string=\"XMLRootName\"\r\n" + " number>6\r\n" + " Data\r\n" + "\r\n" + ""; - private static final String XML = "\r\n" + private static final String XML = + "\r\n" + "\r\n" + " 6\r\n" + " Data\r\n" + "\r\n" + ""; - private static final String JSON_OBJECT = "https://localhost:3000/query?operations[$id]=https%3A%2F%2Fexample.com%2Fperson." + private static final String JSON_OBJECT = + "https://localhost:3000/query?operations[$id]=https%3A%2F%2Fexample.com%2Fperson." + "schema.json&operations[$schema]=https%3A%2F%2Fjson-schema" + ".org%2Fdraft%2F2020-12%2Fschema&operations[title]=Person&operations" + "[type]=object&operations[properties][firstName][type]=string&operations" @@ -92,7 +94,8 @@ public class CoreHelperTest { + "[lastName][description]=The+person%27s+last+name.&operations" + "[properties][age][type]=integer&operations[properties][age][description]" + "=Age+in+years&operations[properties][age][minimum]=0"; - private static final String JSON_VALUE = "https://localhost:3000/query?operations=test-JsonValue"; + private static final String JSON_VALUE = + "https://localhost:3000/query?operations=test-JsonValue"; @Test public void testSerializeNullObject() throws JsonProcessingException { @@ -1044,7 +1047,8 @@ public void testDeserializeAnyOfFailA() throws IOException { @Test(expected = AnyOfValidationException.class) public void testDeserializeAnyOfFailB() throws IOException { - String json = "[{\"NumberOfElectrons\":2},{\"NumberOfElectrons\":2, \"NumberOfProtons\":2}]"; + String json = + "[{\"NumberOfElectrons\":2},{\"NumberOfElectrons\":2, \"NumberOfProtons\":2}]"; ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonNode = objectMapper.readTree(json); try { From 7985a18f416ecab07644fccc349adc18212c28be Mon Sep 17 00:00:00 2001 From: Asad Ali Date: Wed, 24 May 2023 11:03:50 +0500 Subject: [PATCH 4/4] fixed spacing trailing space issue in CoreHelper --- src/main/java/io/apimatic/core/utilities/CoreHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/apimatic/core/utilities/CoreHelper.java b/src/main/java/io/apimatic/core/utilities/CoreHelper.java index c7977aa8..f1933c2f 100644 --- a/src/main/java/io/apimatic/core/utilities/CoreHelper.java +++ b/src/main/java/io/apimatic/core/utilities/CoreHelper.java @@ -180,7 +180,7 @@ public static ObjectMapper getMapper() { /** * Strict Deserialization of Json data. - * + * * @return {@link ObjectMapper}. */ public static ObjectMapper getStrictMapper() {