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