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