2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
20 package org.onap.aai.introspection;
22 import com.att.eelf.configuration.EELFLogger;
23 import com.att.eelf.configuration.EELFManager;
24 import com.google.common.base.CaseFormat;
25 import org.apache.commons.lang.ClassUtils;
26 import org.eclipse.persistence.exceptions.DynamicException;
27 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
28 import org.onap.aai.logging.ErrorLogHelper;
29 import org.onap.aai.logging.LogFormatTools;
30 import org.onap.aai.restcore.MediaType;
31 import org.onap.aai.schema.enums.ObjectMetadata;
32 import org.onap.aai.schema.enums.PropertyMetadata;
33 import org.onap.aai.workarounds.NamingExceptions;
35 import java.io.UnsupportedEncodingException;
36 import java.lang.reflect.InvocationTargetException;
39 public abstract class Introspector implements Cloneable {
41 private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(Introspector.class);
43 protected String className;
44 protected String uriChain = "";
45 protected Loader loader;
46 protected final NamingExceptions namingException = NamingExceptions.getInstance();
47 private Set<String> uniqueProperties = null;
48 private Set<String> indexedProperties = null;
49 private Set<String> allKeys = null;
50 protected Introspector(Object obj) {
53 public abstract boolean hasProperty(String name);
55 protected String convertPropertyName (String name) {
56 return CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, name);
59 protected abstract Object get(String name);
60 protected abstract void set(String name, Object value);
63 * @param name the property name you'd like to retrieve the value for
64 * @return the value of the property
66 public <T> T getValue(String name) {
67 String convertedName = convertPropertyName(name);
70 if (this.hasProperty(name)) {
71 result = this.get(convertedName);
73 /* property not found - slightly ambiguous */
77 Class<?> clazz = this.getClass(name);
78 if (this.isListType(name) && result == null) {
80 this.set(convertedName, clazz.newInstance());
81 result = this.get(convertedName);
82 } catch (DynamicException | InstantiationException | IllegalAccessException e) {
83 LOGGER.warn(e.getMessage(),e);
90 public Introspector getWrappedValue(String name) {
91 String convertedName = convertPropertyName(name);
94 if (this.hasProperty(name)) {
95 value = this.get(convertedName);
97 /* property not found - slightly ambiguous */
101 Class<?> clazz = this.getClass(name);
102 if (this.isListType(name) && value == null) {
104 this.set(convertedName, clazz.newInstance());
105 value = this.get(convertedName);
106 } catch (DynamicException | InstantiationException | IllegalAccessException e) {
107 LOGGER.warn(e.getMessage(),e);
111 return IntrospectorFactory.newInstance(this.getModelType(), value);
119 public List<Introspector> getWrappedListValue(String name) {
120 String convertedName = convertPropertyName(name);
122 List<Introspector> resultList = new ArrayList<>();
123 if (this.hasProperty(name)) {
124 value = this.get(convertedName);
126 /* property not found - slightly ambiguous */
129 boolean isListType = this.isListType(name);
130 if (!this.isListType(name)) {
133 Class<?> clazz = this.getClass(name);
134 if (isListType && value == null) {
136 this.set(convertedName, clazz.newInstance());
137 value = this.get(convertedName);
138 } catch (DynamicException | InstantiationException | IllegalAccessException e) {
139 LOGGER.warn(e.getMessage(),e);
143 List<Object> valueList = (List<Object>)value;
145 for (Object item : valueList) {
146 resultList.add(IntrospectorFactory.newInstance(this.getModelType(), item));
153 public Object castValueAccordingToSchema(String name, Object obj) {
155 Class<?> nameClass = this.getClass(name);
156 if (nameClass == null) {
157 throw new IllegalArgumentException("property: " + name + " does not exist on " + this.getDbName());
162 if (!obj.getClass().getName().equals(nameClass.getName())) {
163 if (nameClass.isPrimitive()) {
164 nameClass = ClassUtils.primitiveToWrapper(nameClass);
165 result = nameClass.getConstructor(String.class).newInstance(obj.toString());
167 if (obj instanceof String) {
168 result = nameClass.getConstructor(String.class).newInstance(obj);
169 } else if (!this.isListType(name) && !this.isComplexType(name)){
170 //box = obj.toString();
171 result = nameClass.getConstructor(String.class).newInstance(obj.toString());
174 } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
175 | InvocationTargetException | NoSuchMethodException | SecurityException e) {
176 ErrorLogHelper.logError("AAI_4017", e.getMessage());
182 public List<Object> castValueAccordingToSchema(String name, List<?> objs) {
183 List<Object> result = new ArrayList<>();
185 for (Object item : objs) {
186 result.add(this.castValueAccordingToSchema(name, item));
194 * @param name the property name you'd like to set the value of
195 * @param obj the value to be set
198 public void setValue(String name, Object obj) throws IllegalArgumentException {
199 Object box = this.castValueAccordingToSchema(name, obj);
201 name = convertPropertyName(name);
206 * @return a list of all the properties available on the object
208 public abstract Set<String> getProperties();
210 public Set<String> getProperties(PropertyPredicate<Introspector, String> p) {
211 final Set<String> temp = new LinkedHashSet<>();
212 this.getProperties().stream().filter(item -> {
213 return p.test(this, item);
217 final Set<String> result = Collections.unmodifiableSet(temp);
224 * @return a list of the required properties on the object
226 public abstract Set<String> getRequiredProperties();
229 * @return a list of the properties that can be used to query the object in the db
231 public abstract Set<String> getKeys();
234 * @return a list of the all key properties for this object
236 public Set<String> getAllKeys() {
237 Set<String> result = null;
238 if (this.allKeys == null) {
239 Set<String> keys = this.getKeys();
240 result = new LinkedHashSet<>();
242 String altKeys = this.getMetadata(ObjectMetadata.ALTERNATE_KEYS_1);
243 if (altKeys != null) {
244 String[] altKeysArray = altKeys.split(",");
245 for (String altKey : altKeysArray) {
249 result = Collections.unmodifiableSet(result);
250 this.allKeys = result;
252 result = this.allKeys;
256 public Set<String> getIndexedProperties() {
257 Set<String> result = null;
259 if (this.indexedProperties == null) {
260 result = new LinkedHashSet<>();
261 Set<String> keys = this.getKeys();
263 String altKeys = this.getMetadata(ObjectMetadata.INDEXED_PROPS);
264 if (altKeys != null) {
265 String[] altKeysArray = altKeys.split(",");
266 for (String altKey : altKeysArray) {
270 this.indexedProperties = Collections.unmodifiableSet(result);
272 result = this.indexedProperties;
276 public Set<String> getUniqueProperties() {
277 Set<String> result = null;
278 if (this.uniqueProperties == null) {
279 String altKeys = this.getMetadata(ObjectMetadata.UNIQUE_PROPS);
280 result = new LinkedHashSet<>();
281 if (altKeys != null) {
282 String[] altKeysArray = altKeys.split(",");
283 for (String altKey : altKeysArray) {
287 this.uniqueProperties = Collections.unmodifiableSet(result);
290 result = this.uniqueProperties;
294 public Set<String> getDependentOn() {
295 String dependentOn = this.getMetadata(ObjectMetadata.DEPENDENT_ON);
296 if (dependentOn == null) {
297 return new LinkedHashSet<>();
299 return new LinkedHashSet<>(Arrays.asList(dependentOn.split(",")));
304 * @return the string name of the java class of the named property
306 public String getType(String name) {
307 Class<?> resultClass = this.getClass(name);
310 if (resultClass != null) {
311 result = resultClass.getName();
312 if (result.equals("java.util.ArrayList")) {
313 result = "java.util.List";
320 * This will returned the generic parameterized type of the underlying
321 * object if it exists
323 * @return the generic type of the java class of the underlying object
325 public String getGenericType(String name) {
326 Class<?> resultClass = this.getGenericTypeClass(name);
329 if (resultClass != null) {
330 result = resultClass.getName();
337 * @return the string name of the java class of the underlying object
339 public abstract String getJavaClassName();
343 * @param name the property name
344 * @return the Class object
346 public abstract Class<?> getClass(String name);
348 public abstract Class<?> getGenericTypeClass(String name);
352 * @param name the property name
353 * @return a new instance of the underlying type of this property
354 * @throws AAIUnknownObjectException
356 public Object newInstanceOfProperty(String name) throws AAIUnknownObjectException {
357 String type = this.getType(name);
358 return loader.objectFromName(type);
361 public Object newInstanceOfNestedProperty(String name) throws AAIUnknownObjectException {
362 String type = this.getGenericType(name);
363 return loader.objectFromName(type);
367 public Introspector newIntrospectorInstanceOfProperty(String name) throws AAIUnknownObjectException {
369 Introspector result = IntrospectorFactory.newInstance(this.getModelType(), this.newInstanceOfProperty(name));
375 public Introspector newIntrospectorInstanceOfNestedProperty(String name) throws AAIUnknownObjectException {
377 Introspector result = IntrospectorFactory.newInstance(this.getModelType(), this.newInstanceOfNestedProperty(name));
383 * Is this type not a Java String or primitive
387 public boolean isComplexType(String name) {
388 String result = this.getType(name);
390 if (result.contains("aai") || result.equals("java.lang.Object")) {
397 public boolean isComplexGenericType(String name) {
398 String result = this.getGenericType(name);
400 if (result.contains("aai")) {
407 public boolean isSimpleType(String name) {
408 return !(this.isComplexType(name) || this.isListType(name));
411 public boolean isSimpleGenericType(String name) {
412 return !this.isComplexGenericType(name);
415 public boolean isListType(String name) {
416 String result = this.getType(name);
418 if (result.contains("java.util.List")) {
425 public boolean isContainer() {
426 Set<String> props = this.getProperties();
427 boolean result = false;
428 if (props.size() == 1 && this.isListType(props.iterator().next())) {
435 public abstract String getChildName();
436 public String getChildDBName() {
437 String result = this.getChildName();
439 result = namingException.getDBName(result);
442 public abstract String getName();
444 public String getDbName() {
445 String lowerHyphen = this.getName();
447 lowerHyphen = namingException.getDBName(lowerHyphen);
452 public abstract ModelType getModelType();
454 public boolean hasChild(Introspector child) {
455 boolean result = false;
456 //check all inheriting types for this child
457 if ("true".equals(this.getMetadata(ObjectMetadata.ABSTRACT))) {
458 String[] inheritors = this.getMetadata(ObjectMetadata.INHERITORS).split(",");
459 for (String inheritor : inheritors) {
461 Introspector temp = this.loader.introspectorFromName(inheritor);
462 result = temp.hasProperty(child.getName());
466 } catch (AAIUnknownObjectException e) {
467 LOGGER.warn("Skipping inheritor " + inheritor + " (Unknown Object) " + LogFormatTools.getStackTop(e));
471 result = this.hasProperty(child.getName());
476 public void setURIChain(String uri) {
479 public abstract String getObjectId() throws UnsupportedEncodingException;
481 public String getURI() throws UnsupportedEncodingException {
482 //String result = this.uriChain;
484 String namespace = this.getMetadata(ObjectMetadata.NAMESPACE);
485 String container = this.getMetadata(ObjectMetadata.CONTAINER);
486 if (this.isContainer()) {
487 result += "/" + this.getName();
490 if (container != null) {
491 result += "/" + container;
493 result += "/" + this.getDbName() + "/" + this.findKey();
495 if (namespace != null && !namespace.equals("")) {
496 result = "/" + namespace + result;
504 public String getGenericURI() {
506 if (this.isContainer()) {
507 result += "/" + this.getName();
509 result += "/" + this.getDbName();
510 for (String key : this.getKeys()) {
511 result += "/{" + this.getDbName() + "-" + key + "}";
518 public String getFullGenericURI() {
520 String namespace = this.getMetadata(ObjectMetadata.NAMESPACE);
521 String container = this.getMetadata(ObjectMetadata.CONTAINER);
522 if (this.isContainer()) {
523 result += "/" + this.getName();
527 if (container != null) {
528 result += "/" + container;
530 result += "/" + this.getDbName();
532 for (String key : this.getKeys()) {
533 result += "/{" + this.getDbName() + "-" + key + "}";
535 if (namespace != null && !namespace.equals("")) {
536 result = "/" + namespace + result;
544 public abstract String preProcessKey(String key);
546 protected abstract String findKey() throws UnsupportedEncodingException;
548 public abstract String marshal(MarshallerProperties properties);
550 public abstract Object clone();
552 public abstract Object getUnderlyingObject();
554 public String marshal(boolean formatted) {
555 MarshallerProperties properties =
556 new MarshallerProperties.Builder(MediaType.APPLICATION_JSON_TYPE).formatted(formatted).build();
558 return marshal(properties);
560 public String makeSingular(String word) {
562 String result = word;
563 result = result.replaceAll("(?:([ho])es|s)$", "");
565 if (result.equals("ClassesOfService")) {
566 result = "ClassOfService";
567 } else if (result.equals("CvlanTag")) {
568 result = "CvlanTagEntry";
569 } else if (result.equals("Metadata")) {
570 result = "Metadatum";
575 protected String makePlural(String word) {
576 String result = word;
578 if (result.equals("cvlan-tag-entry")) {
580 } else if (result.equals("class-of-service")) {
581 return "classes-of-service";
582 } else if (result.equals("metadatum")) {
585 result = result.replaceAll("([a-z])$", "$1s");
586 result = result.replaceAll("([hox])s$", "$1es");
588 if (result.equals("classes-of-services")) {
589 result = "classes-of-service";
595 public abstract String getMetadata(ObjectMetadata metadataName);
596 public abstract Map<PropertyMetadata, String> getPropertyMetadata(String propName);
597 public Optional<String> getPropertyMetadata(String propName, PropertyMetadata metadataName) {
598 final String resultValue = this.getPropertyMetadata(propName).getOrDefault(metadataName, "");
599 Optional<String> result = Optional.empty();
601 if (!resultValue.isEmpty()) {
602 result = Optional.of(resultValue);
608 public abstract Version getVersion();
609 public Loader getLoader() {
613 public boolean isTopLevel() {
615 return this.getMetadata(ObjectMetadata.NAMESPACE) != null;