5632ddf7581bbff0a81baa2f25c9848a2a43ae1b
[aai/aai-common.git] / aai-core / src / main / java / org / openecomp / aai / introspection / PojoStrategy.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * org.openecomp.aai
4  * ================================================================================
5  * Copyright (C) 2017 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.openecomp.aai.introspection;
22
23 import java.io.StringReader;
24 import java.io.StringWriter;
25 import java.lang.reflect.Field;
26 import java.lang.reflect.InvocationTargetException;
27 import java.lang.reflect.Method;
28 import java.lang.reflect.ParameterizedType;
29 import java.lang.reflect.Type;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.Collections;
33 import java.util.HashMap;
34 import java.util.LinkedHashSet;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Set;
38
39 import javax.xml.bind.JAXBContext;
40 import javax.xml.bind.JAXBException;
41 import javax.xml.bind.Marshaller;
42 import javax.xml.bind.Unmarshaller;
43 import javax.xml.bind.annotation.XmlElement;
44 import javax.xml.transform.stream.StreamSource;
45
46 import org.eclipse.persistence.jaxb.UnmarshallerProperties;
47
48 import org.openecomp.aai.annotations.Metadata;
49 import org.openecomp.aai.logging.ErrorLogHelper;
50 import org.openecomp.aai.restcore.MediaType;
51 import org.openecomp.aai.schema.enums.ObjectMetadata;
52 import org.openecomp.aai.schema.enums.PropertyMetadata;
53 import com.google.common.base.CaseFormat;
54 import com.google.common.base.Joiner;
55 import com.google.common.collect.Multimap;
56
57 public class PojoStrategy extends Introspector {
58
59         private Object internalObject = null;
60         private PojoInjestor injestor = null;
61         private Multimap<String, String> keyProps = null;
62         private Metadata classLevelMetadata = null;
63         private Version version;
64         private JAXBContext jaxbContext;
65         private Marshaller marshaller;
66         private Unmarshaller unmarshaller;
67         private Set<String> properties = null;
68         private Set<String> keys = null;
69         private Set<String> requiredProperties = null;
70
71         private boolean isInitialized = false;
72         
73         protected PojoStrategy(Object obj) {
74                 super(obj);
75                 className = PojoStrategy.class.getSimpleName();
76                 this.internalObject = obj;
77                 injestor = new PojoInjestor();
78                 classLevelMetadata = obj.getClass().getAnnotation(Metadata.class);
79
80                 version = injestor.getVersion(obj.getClass().getName());
81                 jaxbContext = injestor.getContextForVersion(version);
82                 super.loader = LoaderFactory.createLoaderForVersion(getModelType(), version);
83                 try {
84                         marshaller = jaxbContext.createMarshaller();
85                         unmarshaller = jaxbContext.createUnmarshaller();
86                 } catch (JAXBException e) {
87
88                 }
89                 
90         }
91
92         private void init() {
93
94                 isInitialized = true;
95
96                 Set<String> properties = new LinkedHashSet<>();
97                 Set<String> keys = new LinkedHashSet<>();
98                 Set<String> required = new LinkedHashSet<>();
99
100                 Field[] fields = this.internalObject.getClass().getDeclaredFields();
101                 
102                 for (Field field : fields) {
103                         if (!field.getName().equals("any")) {
104                                 properties.add(covertFieldToOutputFormat(field.getName()));
105                                 Metadata annotation = field.getAnnotation(Metadata.class);
106                                 XmlElement xmlAnnotation = field.getAnnotation(XmlElement.class);
107                                 if (annotation != null) {
108                                         if (annotation.isKey()) {
109                                                 keys.add(covertFieldToOutputFormat(field.getName()));
110                                         }
111                                 }
112                                 if (xmlAnnotation != null) {
113                                         if (xmlAnnotation.required()) {
114                                                 required.add(covertFieldToOutputFormat(field.getName()));
115                                         }
116                                 }
117                         }
118                 }
119                 properties = Collections.unmodifiableSet(properties);
120                 this.properties = properties;
121                 
122                 keys = Collections.unmodifiableSet(keys);
123                 this.keys = keys;
124                 
125                 required = Collections.unmodifiableSet(required);
126                 this.requiredProperties = required;
127                 
128         }
129         private String covertFieldToOutputFormat(String propName) {
130                 return CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, propName);
131         }
132         
133         @Override
134         public boolean hasProperty(String name) {
135                 //TODO 
136                 return true;
137         }
138         
139         @Override
140         /**
141          * Gets the value of the property via reflection
142          */
143         public Object get(String name) {
144                 String getMethodName = "get" + CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, name);
145                 try {
146                         return this.internalObject.getClass().getDeclaredMethod(getMethodName).invoke(this.internalObject);                     
147                 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
148                         return null;
149                 }
150         }
151
152         @Override
153         public void set(String name, Object value) {
154                 String setMethodName = "set" + CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, name);
155                 try {
156                         this.internalObject.getClass().getDeclaredMethod(setMethodName, value.getClass()).invoke(this.internalObject, value);
157                 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
158                         ErrorLogHelper.logError("AAI_4017", "Error setting name/value pair on POJO: " + e.getMessage());
159                 }
160         }
161
162         @Override
163         public Set<String> getProperties() {
164
165                 if(!isInitialized){
166                         this.init();
167                 }
168                 return this.properties;
169         }
170
171         
172         @Override
173         public Set<String> getRequiredProperties() {
174
175                 if(!isInitialized) {
176                         this.init();
177                 }
178                 return this.requiredProperties;
179         }
180
181         @Override
182         public Set<String> getKeys() {
183
184                 if(!isInitialized){
185                         this.init();
186                 }
187                 return this.keys;
188         }
189
190         public Class<?> getClass(String name) {
191
192                 Field field = null;
193                 try {
194                         field = this.internalObject.getClass().getDeclaredField(CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, name));
195                 } catch (NoSuchFieldException | SecurityException e) {
196                         
197                         return null;
198                 }
199                 
200                 return field.getType();
201         }
202         
203         public Class<?> getGenericTypeClass(String name) {
204                 
205                 try {
206                         String getMethodName = "get" + CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, name);
207                         Method method = internalObject.getClass().getDeclaredMethod(getMethodName);
208                         Type t = method.getGenericReturnType();
209                         if(t instanceof ParameterizedType) {
210                                 ParameterizedType pt = (ParameterizedType)t;
211                                 return ((Class<?>)pt.getActualTypeArguments()[0]);
212                         } else {
213                                 return null;
214                         }
215                         
216                 } catch (Exception e) {
217                         return null;
218                 }
219         }
220
221         @Override
222         public String getJavaClassName() {
223                 return internalObject.getClass().getName();
224         }
225         
226         @Override
227         public Object getUnderlyingObject() {
228                 return this.internalObject;
229         }
230         
231         @Override
232         public String getName() {
233                 String className = internalObject.getClass().getSimpleName();
234                 
235                 return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, className);
236         }
237         
238         @Override
239         protected String findKey() {
240                 Set<String> keys = null;
241                 keys = this.getKeys();
242                 List<String> results = new ArrayList<>();
243                 for (String key : keys) {
244                         if (this.getType(key).toLowerCase().contains("long")) {
245                                 key = ((Long)this.getValue(key)).toString();
246                         } else {
247                                 key = (String)this.getValue(key);
248                         }
249                         results.add(key);
250                 }
251                 
252                 return Joiner.on("/").join(results);
253         }
254         
255         @Override
256         public String marshal(MarshallerProperties properties) {
257                 StringWriter result = new StringWriter();
258         try {
259                 if (properties.getMediaType().equals(MediaType.APPLICATION_JSON_TYPE)) {
260                                 marshaller.setProperty(org.eclipse.persistence.jaxb.MarshallerProperties.MEDIA_TYPE, "application/json");
261                         marshaller.setProperty(org.eclipse.persistence.jaxb.MarshallerProperties.JSON_INCLUDE_ROOT, properties.getIncludeRoot());
262                         marshaller.setProperty(org.eclipse.persistence.jaxb.MarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME, properties.getWrapperAsArrayName());
263                 }
264                 marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, properties.getFormatted());
265                 marshaller.marshal(this.internalObject, result);
266                 } catch (JAXBException e) {
267                         //e.printStackTrace();
268                 }
269
270         return result.toString();
271         }
272         
273         @Override
274         public Object clone() {
275                 Object result = null;
276                  try {
277                                 unmarshaller = jaxbContext.createUnmarshaller();
278
279                         unmarshaller.setProperty(UnmarshallerProperties.MEDIA_TYPE, "application/json");
280                         unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false);
281                                 unmarshaller.setProperty(UnmarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME, true);
282                                 
283                                 result = unmarshaller.unmarshal(new StreamSource(new StringReader(this.marshal(true))), this.internalObject.getClass()).getValue();
284                          } catch (JAXBException e) {
285                                         // TODO Auto-generated catch block
286                                         //e.printStackTrace();
287                         }
288                  result = IntrospectorFactory.newInstance(getModelType(), result);
289                  return result;
290         }
291         
292         @Override
293         public String preProcessKey (String key) {
294                 String result = "";
295                 //String trimmedRestURI = restURI.replaceAll("/[\\w\\-]+?/[\\w\\-]+?$", "");
296                 String[] split = key.split("/");
297                 int i = 0;
298                 for (i = split.length-1; i >= 0; i--) {
299                         
300                         if (keyProps.containsKey(split[i])) {
301                                 break;
302                                 
303                         }
304                         
305                 }
306                 result = Joiner.on("/").join(Arrays.copyOfRange(split, 0, i));
307                 
308                 return result;
309                 
310         }
311         
312         @Override
313         public ModelType getModelType() {
314                 return ModelType.POJO;
315         }
316
317         @Override
318         public String getChildName() {
319                 String className = internalObject.getClass().getSimpleName();
320                 String lowerHyphen = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, className);
321                 
322                 if (this.isContainer()) {
323                         lowerHyphen = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN,this.getGenericTypeClass(this.getProperties().iterator().next()).getSimpleName());
324                 }
325                 
326                 return lowerHyphen;
327         }
328
329         @Override
330         public Map<PropertyMetadata, String> getPropertyMetadata(String prop) {
331                 Field f;
332                 Map<PropertyMetadata, String> result = new HashMap<>();
333                 try {
334                         f = internalObject.getClass().getField(prop);
335                         Metadata m = f.getAnnotation(Metadata.class);
336                         if (m != null) {
337                                 Field[] fields = m.getClass().getFields();
338                                 String fieldName;
339                                 for (Field field : fields) {
340                                         fieldName = field.getName();
341                                         if (fieldName.equals("isAbstract")) {
342                                                 fieldName = "abstract";
343                                         } else if (fieldName.equals("extendsFrom")) {
344                                                 fieldName = "extends";
345                                         }
346                                         fieldName = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, fieldName);
347                                         result.put(PropertyMetadata.valueOf(fieldName), (String)field.get(m));
348                                 }
349                         }
350                 } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
351                         // TODO Auto-generated catch block
352                 }
353                 
354                 return result;
355         }
356
357         @Override
358         public String getObjectId() {
359                 // TODO Auto-generated method stub
360                 return null;
361         }
362         
363         @Override
364         public String getMetadata(ObjectMetadata metadataName) {
365                 String value = null;
366                 String methodName;
367                 if (ObjectMetadata.ABSTRACT.equals(metadataName)) {
368                         methodName = "isAbstract";
369                 } else if (ObjectMetadata.EXTENDS.equals(metadataName)) {
370                         methodName = "extendsFrom";
371                 } else {
372                         methodName = metadataName.toString();
373                 }
374                 
375                 try {
376                         value = (String)this.classLevelMetadata.getClass().getMethod(methodName).invoke(classLevelMetadata);
377                 } catch (IllegalArgumentException | IllegalAccessException | SecurityException | InvocationTargetException | NoSuchMethodException e) {
378                         //TODO
379                 }
380                 
381                 return value;
382         }
383
384         @Override
385         public Version getVersion() {
386                 // TODO Auto-generated method stub
387                 return null;
388         }
389 }