Format Java code with respect to ONAP Code Style
[externalapi/nbi.git] / src / main / java / org / onap / nbi / commons / JacksonFilter.java
1 /**
2  *     Copyright (c) 2018 Orange
3  *
4  *     Licensed under the Apache License, Version 2.0 (the "License");
5  *     you may not use this file except in compliance with the License.
6  *     You may obtain a copy of the License at
7  *
8  *         http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *     Unless required by applicable law or agreed to in writing, software
11  *     distributed under the License is distributed on an "AS IS" BASIS,
12  *     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *     See the License for the specific language governing permissions and
14  *     limitations under the License.
15  */
16
17 package org.onap.nbi.commons;
18
19 import com.fasterxml.jackson.databind.MappingJsonFactory;
20 import java.math.BigDecimal;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collection;
24 import java.util.LinkedHashSet;
25 import java.util.LinkedList;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29 import org.springframework.util.LinkedMultiValueMap;
30 import org.springframework.util.MultiValueMap;
31 import com.fasterxml.jackson.databind.JsonNode;
32 import com.fasterxml.jackson.databind.ObjectMapper;
33 import com.fasterxml.jackson.databind.node.ArrayNode;
34 import com.fasterxml.jackson.databind.node.ObjectNode;
35
36 public class JacksonFilter {
37
38     private static final List<String> SKIPPED_FIELDS = Arrays.asList("internalId");
39
40     private JacksonFilter() {
41     }
42
43     public static <R> List<ObjectNode> createNodes(List<R> list, JsonRepresentation jsonRepresentation) {
44
45         Set<R> set;
46         if (list == null) {
47             set = new LinkedHashSet<>();
48         } else {
49             set = new LinkedHashSet<>(list);
50         }
51         return createNodes(set, jsonRepresentation);
52     }
53
54     public static <R> List<ObjectNode> createNodes(Collection<R> collection, JsonRepresentation jsonRepresentation) {
55         List<ObjectNode> nodeList = new ArrayList<>();
56         for (R element : collection) {
57             ObjectNode node = createNode(element, jsonRepresentation);
58             nodeList.add(node);
59         }
60         return nodeList;
61     }
62
63     public static <R> ObjectNode createNode(R bean, JsonRepresentation jsonRepresentation) {
64         ObjectMapper mapper = new ObjectMapper(new MappingJsonFactory());
65         return JacksonFilter.createNode(mapper, bean, jsonRepresentation.getAttributes());
66     }
67
68     private static <R> ObjectNode createNode(ObjectMapper mapper, R bean, Set<String> names) {
69         // split fieldNames in 2 categories :
70         // simpleFields for simple property names with no '.'
71         // nestedFields for nested property names with a '.'
72         Set<String> simpleFields = new LinkedHashSet<>();
73         MultiValueMap nestedFields = new LinkedMultiValueMap();
74         buildFields(names, simpleFields, nestedFields);
75
76         // create a simple node with only one level containing all simples
77         // properties
78         ObjectNode rootNode = JacksonFilter.createNodeWithSimpleFields(mapper, bean, simpleFields);
79
80         // create nested nodes with deeper levels
81         Set<Map.Entry<String, List<String>>> entrySet = nestedFields.entrySet();
82         // for each nested value, create recursively a node
83         for (Map.Entry<String, List<String>> entry : entrySet) {
84             String rootFieldName = entry.getKey();
85             // add in current node only if full value is not already present in
86             // 1st level
87             if (!simpleFields.contains(rootFieldName)) {
88                 Object nestedBean = BeanUtils.getNestedProperty(bean, rootFieldName);
89                 // add only non null fields
90                 if (nestedBean == null) {
91                     continue;
92                 }
93                 Set<String> nestedFieldNames = new LinkedHashSet<>(entry.getValue());
94                 // current node is an array or a list
95                 if ((nestedBean.getClass().isArray()) || (Collection.class.isAssignableFrom(nestedBean.getClass()))) {
96                     handleListNode(mapper, rootNode, rootFieldName, nestedBean, nestedFieldNames);
97                 } else {
98                     // create recursively a node and add it in current root node
99                     createNodeRecursively(mapper, rootNode, rootFieldName, nestedBean, nestedFieldNames);
100                 }
101             }
102         }
103         return rootNode;
104     }
105
106     private static void createNodeRecursively(ObjectMapper mapper, ObjectNode rootNode, String rootFieldName,
107             Object nestedBean, Set<String> nestedFieldNames) {
108         ObjectNode nestedNode = JacksonFilter.createNode(mapper, nestedBean, nestedFieldNames);
109         if ((nestedNode != null) && (nestedNode.size() > 0)) {
110             rootNode.set(rootFieldName, nestedNode);
111         }
112     }
113
114     private static void buildFields(Set<String> names, Set<String> simpleFields, MultiValueMap nestedFields) {
115         for (String name : names) {
116             int index = name.indexOf('.');
117             boolean isNestedField = (index > 0) && (index < name.length());
118             if (isNestedField) {
119                 String rootFieldName = name.substring(0, index);
120                 String subFieldName = name.substring(index + 1);
121                 nestedFields.add(rootFieldName, subFieldName);
122             } else {
123                 simpleFields.add(name);
124             }
125         }
126     }
127
128     private static void handleListNode(ObjectMapper mapper, ObjectNode rootNode, String rootFieldName,
129             Object nestedBean, Set<String> nestedFieldNames) {
130         Object[] array = null;
131         if ((nestedBean.getClass().isArray())) {
132             array = (Object[]) nestedBean;
133         } else {
134             Collection<?> collection = (Collection<?>) nestedBean;
135             array = collection.toArray();
136         }
137         if (array.length > 0) {
138             // create a node for each element in array
139             // and add created node in an arrayNode
140             Collection<JsonNode> nodes = new LinkedList<>();
141             for (Object object : array) {
142                 ObjectNode nestedNode = JacksonFilter.createNode(mapper, object, nestedFieldNames);
143                 if ((nestedNode != null) && (nestedNode.size() > 0)) {
144                     nodes.add(nestedNode);
145                 }
146             }
147             ArrayNode arrayNode = mapper.createArrayNode();
148             arrayNode.addAll(nodes);
149             if (arrayNode.size() > 0) {
150                 rootNode.set(rootFieldName, arrayNode);
151             }
152         }
153     }
154
155     private static <R> ObjectNode createNodeWithSimpleFields(ObjectMapper mapper, R bean, Set<String> names) {
156         ObjectNode node = mapper.createObjectNode();
157         for (String name : names) {
158             // Prevent getting value of some fields
159             if (SKIPPED_FIELDS.contains(name)) {
160                 continue;
161             }
162
163             JacksonFilter.nodePut(node, name, BeanUtils.getNestedProperty(bean, name));
164         }
165         return node;
166     }
167
168     private static void nodePut(ObjectNode node, String name, Object value) {
169         if (value instanceof Boolean) {
170             node.put(name, (Boolean) value);
171         } else if (value instanceof Integer) {
172             node.put(name, (Integer) value);
173         } else if (value instanceof Long) {
174             node.put(name, (Long) value);
175         } else if (value instanceof Float) {
176             node.put(name, (Float) value);
177         } else if (value instanceof Double) {
178             node.put(name, (Double) value);
179         } else if (value instanceof BigDecimal) {
180             node.put(name, (BigDecimal) value);
181         } else if (value instanceof String) {
182             node.put(name, (String) value);
183         } else {
184             node.putPOJO(name, value);
185         }
186     }
187
188 }