Initial commit with all the necessary files
[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.getClass().getName().equals("java.lang.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 }