Update the license for 2017-2018 license
[aai/aai-common.git] / aai-core / src / main / java / org / onap / aai / introspection / Introspector.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 org.apache.commons.lang.ClassUtils;
26 import org.eclipse.persistence.exceptions.DynamicException;
27 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
28 import org.onap.aai.logging.ErrorLogHelper;
29 import org.onap.aai.logging.LogFormatTools;
30 import org.onap.aai.restcore.MediaType;
31 import org.onap.aai.schema.enums.ObjectMetadata;
32 import org.onap.aai.schema.enums.PropertyMetadata;
33 import org.onap.aai.workarounds.NamingExceptions;
34
35 import java.io.UnsupportedEncodingException;
36 import java.lang.reflect.InvocationTargetException;
37 import java.util.*;
38
39 public abstract class Introspector implements Cloneable {
40
41         private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(Introspector.class);
42
43         protected String className;
44         protected String uriChain = "";
45         protected Loader loader;
46         protected final NamingExceptions namingException = NamingExceptions.getInstance();
47         private Set<String> uniqueProperties = null;
48         private Set<String> indexedProperties = null;
49         private Set<String> allKeys = null;
50         protected Introspector(Object obj) {
51         }
52
53         public abstract boolean hasProperty(String name);
54
55         protected String convertPropertyName (String name) {
56                 return CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, name);
57         }
58         
59         protected abstract Object get(String name);
60         protected abstract void set(String name, Object value);
61         /**
62          * 
63          * @param name the property name you'd like to retrieve the value for
64          * @return the value of the property
65          */
66         public <T> T getValue(String name) {
67                 String convertedName = convertPropertyName(name);
68                 Object result = null;
69                 
70                 if (this.hasProperty(name)) {
71                         result = this.get(convertedName);
72                 } else {
73                         /* property not found - slightly ambiguous */
74                         return null;
75                 }
76                 
77                 Class<?> clazz = this.getClass(name);
78                 if (this.isListType(name) && result == null) {
79                         try {
80                                 this.set(convertedName, clazz.newInstance());
81                                 result = this.get(convertedName);
82                         } catch (DynamicException | InstantiationException | IllegalAccessException e) {
83                                 LOGGER.warn(e.getMessage(),e);
84                         }
85                 }
86
87                 return (T)result;
88         }
89         
90         public Introspector getWrappedValue(String name) {
91                 String convertedName = convertPropertyName(name);
92                 Object value = null;
93                 
94                 if (this.hasProperty(name)) {
95                         value = this.get(convertedName);
96                 } else {
97                         /* property not found - slightly ambiguous */
98                         return null;
99                 }
100                 
101                 Class<?> clazz = this.getClass(name);
102                 if (this.isListType(name) && value == null) {
103                         try {
104                                 this.set(convertedName, clazz.newInstance());
105                                 value = this.get(convertedName);
106                         } catch (DynamicException | InstantiationException | IllegalAccessException e) {
107                                 LOGGER.warn(e.getMessage(),e);
108                         }
109                 }
110                 if (value != null) {
111                         return IntrospectorFactory.newInstance(this.getModelType(), value);
112                 } else {
113                         //no value
114                         return null;
115                 }
116                 
117         }
118         
119         public List<Introspector> getWrappedListValue(String name) {
120                 String convertedName = convertPropertyName(name);
121                 Object value = null;
122                 List<Introspector> resultList = new ArrayList<>();
123                 if (this.hasProperty(name)) {
124                         value = this.get(convertedName);
125                 } else {
126                         /* property not found - slightly ambiguous */
127                         return null;
128                 }
129                 boolean isListType = this.isListType(name);
130                 if (!this.isListType(name)) {
131                         return null;
132                 }
133                 Class<?> clazz = this.getClass(name);
134                 if (isListType && value == null) {
135                         try {
136                                 this.set(convertedName, clazz.newInstance());
137                                 value = this.get(convertedName);
138                         } catch (DynamicException | InstantiationException | IllegalAccessException e) {
139                                 LOGGER.warn(e.getMessage(),e);
140                         }
141                 }
142                 
143                 List<Object> valueList = (List<Object>)value;
144                 
145                 for (Object item : valueList) {
146                         resultList.add(IntrospectorFactory.newInstance(this.getModelType(), item));
147                 }
148                 
149                 return resultList;
150                 
151         }
152         
153         public Object castValueAccordingToSchema(String name, Object obj) {
154                 Object result = obj;
155                 Class<?> nameClass = this.getClass(name);
156                 if (nameClass == null) {
157                         throw new IllegalArgumentException("property: " + name + " does not exist on " + this.getDbName());
158                 }
159                 if (obj != null) {
160         
161                         try {
162                                 if (!obj.getClass().getName().equals(nameClass.getName())) {
163                                         if (nameClass.isPrimitive()) {
164                                                 nameClass = ClassUtils.primitiveToWrapper(nameClass);   
165                                                 result = nameClass.getConstructor(String.class).newInstance(obj.toString());
166                                         }
167                                         if (obj instanceof String) {
168                                                 result = nameClass.getConstructor(String.class).newInstance(obj);
169                                         } else if (!this.isListType(name) && !this.isComplexType(name)){
170                                                 //box = obj.toString();
171                                                 result = nameClass.getConstructor(String.class).newInstance(obj.toString());
172                                         }
173                                 }
174                         } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
175                                         | InvocationTargetException | NoSuchMethodException | SecurityException e) {
176                                 ErrorLogHelper.logError("AAI_4017", e.getMessage());
177                         }
178                 }
179                 return result;
180         }
181         
182         public List<Object> castValueAccordingToSchema(String name, List<?> objs) {
183                 List<Object> result = new ArrayList<>();
184                 
185                 for (Object item : objs) {
186                         result.add(this.castValueAccordingToSchema(name, item));
187                 }
188                 
189                 return result;
190                 
191         }
192         /**
193          * 
194          * @param name the property name you'd like to set the value of
195          * @param obj the value to be set
196          * @return
197          */
198         public void setValue(String name, Object obj) throws IllegalArgumentException {
199                 Object box = this.castValueAccordingToSchema(name, obj);
200                 
201                 name = convertPropertyName(name);
202                 this.set(name, box);
203         }
204         /**
205          * 
206          * @return a list of all the properties available on the object
207          */
208         public abstract Set<String> getProperties();
209         
210         public Set<String> getProperties(PropertyPredicate<Introspector, String> p) {
211                 final Set<String> temp = new LinkedHashSet<>();
212                 this.getProperties().stream().filter(item -> {
213                         return p.test(this, item);
214                 }).forEach(item -> {
215                         temp.add(item);
216                 });
217                 final Set<String> result = Collections.unmodifiableSet(temp);
218                 
219                 return result;
220                 
221         }
222         /**
223          * 
224          * @return a list of the required properties on the object
225          */
226         public abstract Set<String> getRequiredProperties();
227         /**
228          * 
229          * @return a list of the properties that can be used to query the object in the db
230          */
231         public abstract Set<String> getKeys();
232         /**
233          * 
234          * @return a list of the all key properties for this object
235          */
236         public Set<String> getAllKeys() {
237                 Set<String> result = null;
238                 if (this.allKeys == null) {
239                         Set<String> keys = this.getKeys();
240                         result = new LinkedHashSet<>();
241                         result.addAll(keys);
242                         String altKeys = this.getMetadata(ObjectMetadata.ALTERNATE_KEYS_1);
243                         if (altKeys != null) {
244                                 String[] altKeysArray = altKeys.split(",");
245                                 for (String altKey : altKeysArray) {
246                                         result.add(altKey);
247                                 }
248                         }
249                         result = Collections.unmodifiableSet(result);
250                         this.allKeys = result;
251                 }
252                 result = this.allKeys;
253                 return result;
254         }
255
256         public Set<String> getIndexedProperties() {
257                 Set<String> result = null;
258
259                 if (this.indexedProperties == null) {
260                         result = new LinkedHashSet<>();
261                         Set<String> keys = this.getKeys();
262                         result.addAll(keys);
263                         String altKeys = this.getMetadata(ObjectMetadata.INDEXED_PROPS);
264                         if (altKeys != null) {
265                                 String[] altKeysArray = altKeys.split(",");
266                                 for (String altKey : altKeysArray) {
267                                         result.add(altKey);
268                                 }
269                         }
270                         this.indexedProperties = Collections.unmodifiableSet(result);
271                 }
272                 result = this.indexedProperties;
273                 return result;
274         }
275         
276         public Set<String> getUniqueProperties() {
277                 Set<String> result = null;
278                 if (this.uniqueProperties == null) {
279                         String altKeys = this.getMetadata(ObjectMetadata.UNIQUE_PROPS);
280                         result = new LinkedHashSet<>();
281                         if (altKeys != null) {
282                                 String[] altKeysArray = altKeys.split(",");
283                                 for (String altKey : altKeysArray) {
284                                         result.add(altKey);
285                                 }
286                         }
287                         this.uniqueProperties = Collections.unmodifiableSet(result);
288
289                 }
290                 result = this.uniqueProperties;
291                 return result;
292         }
293         
294         public Set<String> getDependentOn() {
295                 String dependentOn = this.getMetadata(ObjectMetadata.DEPENDENT_ON);
296                 if (dependentOn == null) {
297                         return  new LinkedHashSet<>();
298                 }
299                 return new LinkedHashSet<>(Arrays.asList(dependentOn.split(",")));
300         }
301         /**
302          * 
303          * @param name
304          * @return the string name of the java class of the named property
305          */
306         public String getType(String name) {
307                 Class<?> resultClass = this.getClass(name);
308                 String result = "";
309                 
310                 if (resultClass != null) {
311                         result = resultClass.getName();
312                         if (result.equals("java.util.ArrayList")) {
313                                 result = "java.util.List";
314                         }
315                 }
316                 
317                 return result;
318         }
319         /**
320          * This will returned the generic parameterized type of the underlying
321          * object if it exists
322          * @param name
323          * @return the generic type of the java class of the underlying object
324          */
325         public String getGenericType(String name) {
326                 Class<?> resultClass = this.getGenericTypeClass(name);
327                 String result = "";
328                 
329                 if (resultClass != null) {
330                         result = resultClass.getName();
331                 }
332                 
333                 return result;
334         }
335         /**
336          * 
337          * @return the string name of the java class of the underlying object
338          */
339         public abstract String getJavaClassName();
340         
341         /**
342          * 
343          * @param name the property name
344          * @return the Class object
345          */
346         public abstract Class<?> getClass(String name);
347         
348         public abstract Class<?> getGenericTypeClass(String name);
349
350         /**
351          * 
352          * @param name the property name
353          * @return a new instance of the underlying type of this property
354          * @throws AAIUnknownObjectException 
355          */
356         public Object newInstanceOfProperty(String name) throws AAIUnknownObjectException {
357                 String type = this.getType(name);
358                 return loader.objectFromName(type);
359         }
360
361         public Object newInstanceOfNestedProperty(String name) throws AAIUnknownObjectException {
362                 String type = this.getGenericType(name);
363                 return loader.objectFromName(type);
364         }
365         
366         
367         public Introspector newIntrospectorInstanceOfProperty(String name) throws AAIUnknownObjectException {
368         
369                 Introspector result = IntrospectorFactory.newInstance(this.getModelType(), this.newInstanceOfProperty(name));
370                 
371                 return result;
372                 
373         }
374         
375         public Introspector newIntrospectorInstanceOfNestedProperty(String name) throws AAIUnknownObjectException {
376         
377                 Introspector result = IntrospectorFactory.newInstance(this.getModelType(), this.newInstanceOfNestedProperty(name));
378                 
379                 return result;
380                 
381         }
382         /**
383          * Is this type not a Java String or primitive
384          * @param name
385          * @return
386          */
387         public boolean isComplexType(String name) {
388                 String result = this.getType(name);
389                 
390                 if (result.contains("aai") || result.equals("java.lang.Object")) {
391                         return true;
392                 } else {
393                         return false;
394                 }
395         }
396         
397         public boolean isComplexGenericType(String name) {
398                 String result = this.getGenericType(name);
399                 
400                 if (result.contains("aai")) {
401                         return true;
402                 } else {
403                         return false;
404                 }
405         }
406         
407         public boolean isSimpleType(String name) {
408                 return !(this.isComplexType(name) || this.isListType(name));
409         }
410         
411         public boolean isSimpleGenericType(String name) {
412                 return !this.isComplexGenericType(name);
413         }
414
415         public boolean isListType(String name) {
416                 String result = this.getType(name);
417                 
418                 if (result.contains("java.util.List")) {
419                         return true;
420                 } else {
421                         return false;
422                 }
423         }
424         
425         public boolean isContainer() {
426                 Set<String> props = this.getProperties();
427                 boolean result = false;
428                 if (props.size() == 1 && this.isListType(props.iterator().next())) {
429                         result = true;
430                 }
431                 
432                 return result;
433         }
434         
435         public abstract String getChildName();
436         public String getChildDBName() {
437                 String result = this.getChildName();
438                 
439                 result = namingException.getDBName(result);
440                 return result;
441         }
442         public abstract String getName();
443
444         public String getDbName() {
445                 String lowerHyphen = this.getName();
446
447                 lowerHyphen = namingException.getDBName(lowerHyphen);
448                 
449                 return lowerHyphen;
450         }
451                 
452         public abstract ModelType getModelType();
453
454         public boolean hasChild(Introspector child) {
455                 boolean result = false;
456                 //check all inheriting types for this child
457                 if ("true".equals(this.getMetadata(ObjectMetadata.ABSTRACT))) {
458                         String[] inheritors = this.getMetadata(ObjectMetadata.INHERITORS).split(",");
459                         for (String inheritor : inheritors) {
460                                 try {
461                                         Introspector temp = this.loader.introspectorFromName(inheritor);
462                                         result = temp.hasProperty(child.getName());
463                                         if (result) {
464                                                 break;
465                                         }
466                                 } catch (AAIUnknownObjectException e) {
467                                         LOGGER.warn("Skipping inheritor " + inheritor + " (Unknown Object) " + LogFormatTools.getStackTop(e));
468                                 }
469                         }
470                 } else {
471                         result = this.hasProperty(child.getName());
472                 }
473                 return result;
474         }
475         
476         public void setURIChain(String uri) {
477                 this.uriChain = uri;
478         }
479         public abstract String getObjectId() throws UnsupportedEncodingException;
480
481         public String getURI() throws UnsupportedEncodingException {
482                 //String result = this.uriChain;
483                 String result = "";
484                 String namespace = this.getMetadata(ObjectMetadata.NAMESPACE);
485                 String container = this.getMetadata(ObjectMetadata.CONTAINER);
486                 if (this.isContainer()) {
487                          result += "/" + this.getName();
488                 } else {
489                         
490                         if (container != null) {
491                                 result += "/" + container;
492                         }
493                         result += "/" + this.getDbName() + "/" + this.findKey();
494                         
495                         if (namespace != null && !namespace.equals("")) {
496                                 result = "/" + namespace + result;
497                         }
498                 }
499                 
500
501                 return result;
502         }
503         
504         public String getGenericURI() {
505                 String result = "";
506                 if (this.isContainer()) {
507                          result += "/" + this.getName();
508                 } else {
509                         result += "/" + this.getDbName();
510                         for (String key : this.getKeys()) {
511                                 result += "/{" + this.getDbName() + "-" + key + "}";
512                         }
513                 }
514                 
515                 return result;
516         }
517         
518         public String getFullGenericURI() {
519                 String result = "";
520                 String namespace = this.getMetadata(ObjectMetadata.NAMESPACE);
521                 String container = this.getMetadata(ObjectMetadata.CONTAINER);
522                 if (this.isContainer()) {
523                          result += "/" + this.getName();
524                 } else {
525
526
527                         if (container != null) {
528                                 result += "/" + container;
529                         }
530                         result += "/" + this.getDbName();
531
532                         for (String key : this.getKeys()) {
533                                 result += "/{" + this.getDbName() + "-" + key + "}";
534                         }
535                         if (namespace != null && !namespace.equals("")) {
536                                 result = "/" + namespace + result;
537                         }
538                         
539                 }
540                 
541                 return result;
542         }
543
544         public abstract String preProcessKey(String key);
545         
546         protected abstract String findKey() throws UnsupportedEncodingException;
547         
548         public abstract String marshal(MarshallerProperties properties);
549         
550         public abstract Object clone();
551
552         public abstract Object getUnderlyingObject();
553         
554         public String marshal(boolean formatted) {
555                 MarshallerProperties properties =
556                                 new MarshallerProperties.Builder(MediaType.APPLICATION_JSON_TYPE).formatted(formatted).build();
557                 
558                 return marshal(properties);
559         }
560         public String makeSingular(String word) {
561                 
562                 String result = word;
563                 result = result.replaceAll("(?:([ho])es|s)$", "");
564                 
565                 if (result.equals("ClassesOfService")) {
566                         result = "ClassOfService";
567                 } else if (result.equals("CvlanTag")) {
568                         result = "CvlanTagEntry";
569                 } else if (result.equals("Metadata")) {
570                         result = "Metadatum";
571                 }
572                 return result;
573         }
574         
575         protected String makePlural(String word) {
576                 String result = word;
577                 
578                 if (result.equals("cvlan-tag-entry")) {
579                         return "cvlan-tags";
580                 } else if (result.equals("class-of-service")) {
581                         return "classes-of-service";
582                 } else if (result.equals("metadatum")) {
583                         return "metadata";
584                 }
585                 result = result.replaceAll("([a-z])$", "$1s");
586                 result = result.replaceAll("([hox])s$", "$1es");
587                 /*
588                 if (result.equals("classes-of-services")) {
589                         result = "classes-of-service";
590                 }*/
591                 
592                 return result;
593         }
594
595         public abstract String getMetadata(ObjectMetadata metadataName);
596         public abstract Map<PropertyMetadata, String> getPropertyMetadata(String propName);
597         public Optional<String> getPropertyMetadata(String propName, PropertyMetadata metadataName) {
598                 final String resultValue = this.getPropertyMetadata(propName).getOrDefault(metadataName, "");
599                 Optional<String> result = Optional.empty();
600                 
601                 if (!resultValue.isEmpty()) {
602                         result = Optional.of(resultValue);
603                 }
604                 return result;
605                 
606         }
607
608         public abstract Version getVersion();
609         public Loader getLoader() {
610                 return this.loader;
611         }
612         
613         public boolean isTopLevel() {
614                 
615                 return this.getMetadata(ObjectMetadata.NAMESPACE) != null;
616         }
617 }