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