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