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