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