Changes for checkstyle 8.32
[policy/apex-pdp.git] / context / context-management / src / main / java / org / onap / policy / apex / context / impl / schema / java / JavaSchemaHelper.java
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
4  *  Modifications Copyright (C) 2019-2020 Nordix Foundation.
5  * ================================================================================
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * SPDX-License-Identifier: Apache-2.0
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.policy.apex.context.impl.schema.java;
23
24 import com.google.gson.Gson;
25 import com.google.gson.GsonBuilder;
26 import com.google.gson.JsonElement;
27 import java.lang.reflect.Constructor;
28 import java.util.HashMap;
29 import java.util.Map;
30 import org.onap.policy.apex.context.ContextRuntimeException;
31 import org.onap.policy.apex.context.impl.schema.AbstractSchemaHelper;
32 import org.onap.policy.apex.context.parameters.ContextParameterConstants;
33 import org.onap.policy.apex.context.parameters.SchemaParameters;
34 import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
35 import org.onap.policy.apex.model.contextmodel.concepts.AxContextSchema;
36 import org.onap.policy.common.parameters.ParameterService;
37 import org.slf4j.ext.XLogger;
38 import org.slf4j.ext.XLoggerFactory;
39
40 /**
41  * This class implements translation to and from Apex distributed objects and Java objects when a Java schema is used.
42  * It creates schema items as Java objects and marshals and unmarshals these objects in various formats. All objects
43  * must be of the type of Java class defined in the schema.
44  *
45  * @author Liam Fallon (liam.fallon@ericsson.com)
46  */
47 public class JavaSchemaHelper extends AbstractSchemaHelper {
48     // Get a reference to the logger
49     private static final XLogger LOGGER = XLoggerFactory.getXLogger(JavaSchemaHelper.class);
50
51     // This map defines the built in types in types in Java
52     // @formatter:off
53     private static final Map<String, Class<?>> BUILT_IN_MAP = new HashMap<>();
54
55     static {
56         BUILT_IN_MAP.put("int",    Integer.TYPE);
57         BUILT_IN_MAP.put("long",   Long.TYPE);
58         BUILT_IN_MAP.put("double", Double.TYPE);
59         BUILT_IN_MAP.put("float",  Float.TYPE);
60         BUILT_IN_MAP.put("bool",   Boolean.TYPE);
61         BUILT_IN_MAP.put("char",   Character.TYPE);
62         BUILT_IN_MAP.put("byte",   Byte.TYPE);
63         BUILT_IN_MAP.put("void",   Void.TYPE);
64         BUILT_IN_MAP.put("short",  Short.TYPE);
65     }
66     // @formatter:on
67
68     /**
69      * {@inheritDoc}.
70      */
71     @Override
72     public void init(final AxKey userKey, final AxContextSchema schema) {
73         super.init(userKey, schema);
74
75         final String javatype = schema.getSchema();
76         // For Java, the schema is the Java class canonical path
77
78         try {
79             setSchemaClass(Class.forName(schema.getSchema()));
80         } catch (final Exception e) {
81
82             String resultSting = userKey.getId() + ": class/type " + schema.getSchema() + " for context schema \""
83                     + schema.getId() + "\" not found.";
84             if (JavaSchemaHelper.BUILT_IN_MAP.get(javatype) != null) {
85                 resultSting += " Primitive types are not supported. Use the appropriate Java boxing type instead.";
86             } else {
87                 resultSting += " Check the class path of the JVM";
88             }
89             LOGGER.warn(resultSting);
90             throw new ContextRuntimeException(resultSting, e);
91         }
92     }
93
94     /**
95      * {@inheritDoc}.
96      */
97     @Override
98     public Object createNewInstance(final Object incomingObject) {
99         if (incomingObject == null) {
100             return null;
101         }
102
103         if (getSchemaClass() == null) {
104             final String returnString =
105                     getUserKey().getId() + ": could not create an instance, schema class for the schema is null";
106             LOGGER.warn(returnString);
107             throw new ContextRuntimeException(returnString);
108         }
109
110         if (incomingObject instanceof JsonElement) {
111             final String elementJsonString = getGson().toJson((JsonElement) incomingObject);
112             return getGson().fromJson(elementJsonString, this.getSchemaClass());
113         }
114
115         if (getSchemaClass().isAssignableFrom(incomingObject.getClass())) {
116             return incomingObject;
117         }
118
119         final String returnString = getUserKey().getId() + ": the object \"" + incomingObject + "\" of type \""
120                 + incomingObject.getClass().getName()
121                 + "\" is not an instance of JsonObject and is not assignable to \"" + getSchemaClass().getName() + "\"";
122         LOGGER.warn(returnString);
123         throw new ContextRuntimeException(returnString);
124     }
125
126     /**
127      * {@inheritDoc}.
128      */
129     @Override
130     public Object unmarshal(final Object object) {
131         if (object == null) {
132             return null;
133         }
134
135         // If the object is an instance of the incoming object, carry on
136         if (object.getClass().equals(getSchemaClass())) {
137             return object;
138         }
139
140         // For numeric types, do a numeric conversion
141         if (Number.class.isAssignableFrom(getSchemaClass())) {
142             return numericConversion(object);
143         }
144
145         if (getSchemaClass().isAssignableFrom(object.getClass())) {
146             return object;
147         } else {
148             return stringConversion(object);
149         }
150     }
151
152     /**
153      * {@inheritDoc}.
154      */
155     @Override
156     public String marshal2String(final Object schemaObject) {
157         if (schemaObject == null) {
158             return "null";
159         }
160
161         // Check the incoming object is of a correct class
162         if (getSchemaClass().isAssignableFrom(schemaObject.getClass())) {
163             // Use Gson to translate the object
164             return getGson().toJson(schemaObject);
165         } else {
166             final String returnString = getUserKey().getId() + ": object \"" + schemaObject.toString()
167                     + "\" of class \"" + schemaObject.getClass().getName() + "\" not compatible with class \""
168                     + getSchemaClass().getName() + "\"";
169             LOGGER.warn(returnString);
170             throw new ContextRuntimeException(returnString);
171         }
172     }
173
174     /**
175      * {@inheritDoc}.
176      */
177     @Override
178     public Object marshal2Object(final Object schemaObject) {
179         // Use Gson to marshal the schema object into a Json element to return
180         return getGson().toJsonTree(schemaObject, getSchemaClass());
181     }
182
183     /**
184      * Do a numeric conversion between numeric types.
185      *
186      * @param object The incoming numeric object
187      * @return The converted object
188      */
189     private Object numericConversion(final Object object) {
190         // Check if the incoming object is a number, if not do a string conversion
191         if (object instanceof Number) {
192             if (getSchemaClass().isAssignableFrom(Byte.class)) {
193                 return ((Number) object).byteValue();
194             } else if (getSchemaClass().isAssignableFrom(Short.class)) {
195                 return ((Number) object).shortValue();
196             } else if (getSchemaClass().isAssignableFrom(Integer.class)) {
197                 return ((Number) object).intValue();
198             } else if (getSchemaClass().isAssignableFrom(Long.class)) {
199                 return ((Number) object).longValue();
200             } else if (getSchemaClass().isAssignableFrom(Float.class)) {
201                 return ((Number) object).floatValue();
202             } else if (getSchemaClass().isAssignableFrom(Double.class)) {
203                 return ((Number) object).doubleValue();
204             }
205         }
206
207         // OK, we'll try and convert from a string representation of the incoming object
208         return stringConversion(object);
209     }
210
211     /**
212      * Do a string conversion to the class type.
213      *
214      * @param object The incoming numeric object
215      * @return The converted object
216      */
217     private Object stringConversion(final Object object) {
218         // OK, we'll try and convert from a string representation of the incoming object
219         try {
220             final Constructor<?> stringConstructor = getSchemaClass().getConstructor(String.class);
221             return stringConstructor.newInstance(object.toString());
222         } catch (final Exception e) {
223             final String returnString = getUserKey().getId() + ": object \"" + object.toString() + "\" of class \""
224                     + object.getClass().getName() + "\" not compatible with class \"" + getSchemaClass().getName()
225                     + "\"";
226             LOGGER.warn(returnString, e);
227             throw new ContextRuntimeException(returnString);
228         }
229     }
230
231     /**
232      * Get a GSON instance that has the correct adaptation included.
233      *
234      * @return the GSON instance
235      */
236     private Gson getGson() {
237         GsonBuilder gsonBuilder = new GsonBuilder().setPrettyPrinting();
238
239         // Get the Java schema helper parameters from the parameter service
240         SchemaParameters schemaParameters = ParameterService.get(ContextParameterConstants.SCHEMA_GROUP_NAME);
241
242         JavaSchemaHelperParameters javaSchemaHelperParmeters =
243                 (JavaSchemaHelperParameters) schemaParameters.getSchemaHelperParameterMap().get("Java");
244
245         if (javaSchemaHelperParmeters == null) {
246             javaSchemaHelperParmeters = new JavaSchemaHelperParameters();
247         }
248
249         for (JavaSchemaHelperJsonAdapterParameters jsonAdapterEntry : javaSchemaHelperParmeters.getJsonAdapters()
250                 .values()) {
251
252             Object adapterObject;
253             try {
254                 adapterObject = jsonAdapterEntry.getAdaptorClazz().getDeclaredConstructor().newInstance();
255             } catch (Exception e) {
256                 final String returnString = getUserKey().getId() + ": instantiation of adapter class \""
257                         + jsonAdapterEntry.getAdaptorClass() + "\"  to decode and encode class \""
258                         + jsonAdapterEntry.getAdaptedClass() + "\" failed: " + e.getMessage();
259                 LOGGER.warn(returnString, e);
260                 throw new ContextRuntimeException(returnString);
261             }
262
263             gsonBuilder.registerTypeAdapter(jsonAdapterEntry.getAdaptedClazz(), adapterObject);
264         }
265
266         return gsonBuilder.create();
267     }
268 }