15311638f656ce1e18ff5484fa533854df7f5ca6
[aai/aai-common.git] / aai-core / src / main / java / org / onap / aai / introspection / MoxyStrategy.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *    http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.aai.introspection;
22
23 import com.att.eelf.configuration.EELFLogger;
24 import com.att.eelf.configuration.EELFManager;
25 import com.google.common.base.CaseFormat;
26 import com.google.common.base.Joiner;
27
28 import java.io.StringWriter;
29 import java.io.UnsupportedEncodingException;
30 import java.util.*;
31 import java.util.Map.Entry;
32
33 import javax.xml.bind.JAXBException;
34 import javax.xml.bind.Marshaller;
35
36 import org.eclipse.persistence.descriptors.ClassDescriptor;
37 import org.eclipse.persistence.dynamic.DynamicEntity;
38 import org.eclipse.persistence.dynamic.DynamicType;
39 import org.eclipse.persistence.exceptions.DynamicException;
40 import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext;
41 import org.eclipse.persistence.mappings.DatabaseMapping;
42 import org.eclipse.persistence.oxm.XMLField;
43 import org.eclipse.persistence.oxm.mappings.XMLCompositeCollectionMapping;
44 import org.eclipse.persistence.oxm.mappings.XMLCompositeDirectCollectionMapping;
45 import org.onap.aai.config.SpringContextAware;
46 import org.onap.aai.logging.LogFormatTools;
47 import org.onap.aai.nodes.CaseFormatStore;
48 import org.onap.aai.nodes.NodeIngestor;
49 import org.onap.aai.restcore.MediaType;
50 import org.onap.aai.schema.enums.ObjectMetadata;
51 import org.onap.aai.schema.enums.PropertyMetadata;
52 import org.onap.aai.setup.SchemaVersion;
53 import org.springframework.web.util.UriUtils;
54
55 public class MoxyStrategy extends Introspector {
56
57     private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(MoxyStrategy.class);
58     private DynamicEntity internalObject = null;
59     private DynamicType internalType = null;
60     private DynamicJAXBContext jaxbContext = null;
61     private ClassDescriptor cd = null;
62     private SchemaVersion version = null;
63     private Set<String> properties = null;
64     private Set<String> keys = null;
65     private Set<String> requiredProperties = null;
66
67     private boolean isInitialized = false;
68
69     protected MoxyStrategy(Object obj) {
70         super(obj);
71         /* must look up the correct jaxbcontext for this object */
72         className = MoxyStrategy.class.getSimpleName();
73         internalObject = (DynamicEntity) obj;
74         version = nodeIngestor.getVersionFromClassName(internalObject.getClass().getName());
75         super.loader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(getModelType(), version);
76         jaxbContext = nodeIngestor.getContextForVersion(version);
77         String simpleName = internalObject.getClass().getName();
78         internalType = jaxbContext.getDynamicType(simpleName);
79
80         cd = internalType.getDescriptor();
81     }
82
83     private void init() {
84         isInitialized = true;
85
86         Set<String> props = new LinkedHashSet<>();
87         for (String s : internalType.getPropertiesNames()) {
88             String value = caseFormatStore.fromLowerCamelToLowerHyphen(s).orElseGet(() -> {
89                 LOGGER.debug("Unable to find {} in the store from lower camel to lower hyphen", s);
90                 return CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, s);
91             });
92             props.add(value);
93
94         }
95         props = Collections.unmodifiableSet(props);
96         this.properties = props;
97
98         Set<String> requiredProps = new LinkedHashSet<>();
99         for (DatabaseMapping dm : cd.getMappings()) {
100             if (dm.getField() instanceof XMLField) {
101                 XMLField x = (XMLField) dm.getField();
102                 if (x != null && x.isRequired()) {
103                     requiredProps.add(this.removeXPathDescriptor(x.getName()));
104                 }
105             }
106         }
107         requiredProps = Collections.unmodifiableSet(requiredProps);
108         this.requiredProperties = requiredProps;
109
110         Set<String> keys = new LinkedHashSet<>();
111
112         for (String name : internalType.getDescriptor().getPrimaryKeyFieldNames()) {
113             keys.add(this.removeXPathDescriptor(name));
114         }
115         keys = Collections.unmodifiableSet(keys);
116         this.keys = keys;
117
118     }
119
120     @Override
121     public boolean hasProperty(String name) {
122         String convertedName = convertPropertyName(name);
123
124         return internalType.containsProperty(convertedName);
125     }
126
127     @Override
128     public Object get(String name) {
129         return internalObject.get(name);
130     }
131
132     @Override
133     public void set(String name, Object obj) {
134
135         internalObject.set(name, obj);
136     }
137
138     @Override
139     public Set<String> getProperties() {
140
141         if (!isInitialized) {
142             init();
143         }
144
145         return this.properties;
146
147     }
148
149     @Override
150     public Set<String> getRequiredProperties() {
151
152         if (!isInitialized) {
153             init();
154         }
155
156         return this.requiredProperties;
157     }
158
159     @Override
160     public Set<String> getKeys() {
161
162         if (!isInitialized) {
163             init();
164         }
165
166         return this.keys;
167     }
168
169     @Override
170     public Map<PropertyMetadata, String> getPropertyMetadata(String prop) {
171         String propName = this.convertPropertyName(prop);
172         DatabaseMapping mapping = cd.getMappingForAttributeName(propName);
173         Map<PropertyMetadata, String> result = new HashMap<>();
174         if (mapping != null) {
175             Set<Entry> entrySet = mapping.getProperties().entrySet();
176             for (Entry<?, ?> entry : entrySet) {
177                 result.put(
178                         PropertyMetadata.valueOf(
179                                 CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, (String) entry.getKey())),
180                         (String) entry.getValue());
181             }
182         }
183
184         return result;
185     }
186
187     @Override
188     public String getJavaClassName() {
189         return internalObject.getClass().getName();
190     }
191
192     @Override
193     public Class<?> getClass(String name) {
194         name = convertPropertyName(name);
195         Class<?> resultClass = null;
196         try {
197             if (internalType.getPropertyType(name) == null) {
198                 if (cd.getMappingForAttributeName(name) instanceof XMLCompositeDirectCollectionMapping) {
199                     resultClass = cd.getMappingForAttributeName(name).getContainerPolicy().getContainerClass();
200
201                 } else if (cd.getMappingForAttributeName(name) instanceof XMLCompositeCollectionMapping) {
202                     resultClass = cd.getMappingForAttributeName(name).getContainerPolicy().getContainerClass();
203                 } else {
204                     ClassDescriptor referenceDiscriptor = cd.getMappingForAttributeName(name).getReferenceDescriptor();
205                     if (referenceDiscriptor != null) {
206                         resultClass = referenceDiscriptor.getJavaClass();
207                     } else {
208                         resultClass = Object.class;
209                     }
210                 }
211             } else {
212                 resultClass = internalType.getPropertyType(name);
213             }
214         } catch (DynamicException e) {
215             // property doesn't exist
216         }
217         return resultClass;
218     }
219
220     @Override
221     public Class<?> getGenericTypeClass(String name) {
222         name = convertPropertyName(name);
223         Class<?> resultClass = null;
224         if (internalType.getPropertyType(name) == null) {
225             if (cd.getMappingForAttributeName(name) instanceof XMLCompositeDirectCollectionMapping) {
226                 resultClass = cd.getMappingForAttributeName(name).getFields().get(0).getType();
227
228             } else if (cd.getMappingForAttributeName(name) instanceof XMLCompositeCollectionMapping) {
229                 resultClass = cd.getMappingForAttributeName(name).getReferenceDescriptor().getJavaClass();
230             }
231         }
232
233         return resultClass;
234     }
235
236     @Override
237     public Object getUnderlyingObject() {
238         return this.internalObject;
239     }
240
241     @Override
242     public String getChildName() {
243
244         String className = internalObject.getClass().getSimpleName();
245         String lowerHyphen = caseFormatStore.fromUpperCamelToLowerHyphen(className).orElseGet(() -> {
246             LOGGER.debug("Unable to find {} in the store for upper camel to lower hyphen", className);
247             return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, className);
248         });
249
250         if (this.isContainer()) {
251             String upperCamel = this.getGenericTypeClass(this.getProperties().iterator().next()).getSimpleName();
252
253             lowerHyphen = caseFormatStore.fromUpperCamelToLowerHyphen(upperCamel).orElseGet(() -> {
254                 LOGGER.debug("Unable to find {} in the store for upper camel to lower hyphen", upperCamel);
255                 return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, upperCamel);
256             });
257         }
258
259         return lowerHyphen;
260     }
261
262     @Override
263     public String getName() {
264         String className = internalObject.getClass().getSimpleName();
265         return caseFormatStore.fromUpperCamelToLowerHyphen(className).orElseGet(() -> {
266             LOGGER.debug("Unable to find {} in the store for upper camel to lower hyphen", className);
267             return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, className);
268         });
269     }
270
271     @Override
272     public String getObjectId() throws UnsupportedEncodingException {
273         String result = "";
274         String container = this.getMetadata(ObjectMetadata.CONTAINER);
275         if (this.isContainer()) {
276             result += "/" + this.getName();
277         } else {
278
279             if (container != null) {
280                 result += "/" + container;
281             }
282             result += "/" + this.getDbName() + "/" + this.findKey();
283
284         }
285
286         return result;
287     }
288
289     @Override
290     protected String findKey() throws UnsupportedEncodingException {
291         Set<String> keys = null;
292         keys = this.getKeys();
293         List<String> results = new ArrayList<>();
294         for (String key : keys) {
295             String value = UriUtils.encode(this.getValue(key).toString(), "UTF-8");
296             results.add(value);
297         }
298
299         return Joiner.on("/").join(results);
300     }
301
302     @Override
303     public String preProcessKey(String key) {
304         String result = "";
305         String[] split = key.split("/");
306         int i = 0;
307         for (i = split.length - 1; i >= 0; i--) {
308
309             if (jaxbContext.getDynamicType(split[i]) != null) {
310                 break;
311
312             }
313
314         }
315         result = Joiner.on("/").join(Arrays.copyOfRange(split, 0, i));
316
317         return result;
318
319     }
320
321     @Override
322     public String marshal(MarshallerProperties properties) {
323         StringWriter result = new StringWriter();
324         try {
325             Marshaller marshaller = jaxbContext.createMarshaller();
326             if (properties.getMediaType().equals(MediaType.APPLICATION_JSON_TYPE)) {
327                 marshaller.setProperty(org.eclipse.persistence.jaxb.MarshallerProperties.MEDIA_TYPE,
328                         "application/json");
329                 marshaller.setProperty(org.eclipse.persistence.jaxb.MarshallerProperties.JSON_INCLUDE_ROOT,
330                         properties.getIncludeRoot());
331                 marshaller.setProperty(org.eclipse.persistence.jaxb.MarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME,
332                         properties.getWrapperAsArrayName());
333                 marshaller.setProperty(org.eclipse.persistence.jaxb.MarshallerProperties.JSON_MARSHAL_EMPTY_COLLECTIONS,
334                         false);
335             }
336
337             marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, properties.getFormatted());
338             marshaller.marshal(this.internalObject, result);
339         } catch (JAXBException e) {
340             LOGGER.warn("Encountered an jaxb exception during marshalling ", LogFormatTools.getStackTop(e));
341         }
342
343         return result.toString();
344     }
345
346     @Override
347     public ModelType getModelType() {
348         return ModelType.MOXY;
349     }
350
351     private String removeXPathDescriptor(String name) {
352
353         return name.replaceAll("/text\\(\\)", "");
354     }
355
356     @Override
357     public String getMetadata(ObjectMetadata name) {
358
359         return (String) cd.getProperty(name.toString());
360     }
361
362     @Override
363     public SchemaVersion getVersion() {
364
365         return this.version;
366     }
367 }