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