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.config.SpringContextAware;
28 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
29 import org.onap.aai.logging.ErrorLogHelper;
30 import org.onap.aai.logging.LogFormatTools;
31 import org.onap.aai.nodes.CaseFormatStore;
32 import org.onap.aai.nodes.NodeIngestor;
33 import org.onap.aai.restcore.MediaType;
34 import org.onap.aai.schema.enums.ObjectMetadata;
35 import org.onap.aai.schema.enums.PropertyMetadata;
36 import org.onap.aai.setup.SchemaVersion;
37 import org.onap.aai.workarounds.NamingExceptions;
39 import java.io.UnsupportedEncodingException;
40 import java.lang.reflect.InvocationTargetException;
42 import java.util.stream.Collectors;
45 public abstract class Introspector implements Cloneable {
47 private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(Introspector.class);
49 protected String className;
50 protected String uriChain = "";
51 protected Loader loader;
52 protected final NamingExceptions namingException = NamingExceptions.getInstance();
53 private Set<String> uniqueProperties = null;
54 private Set<String> indexedProperties = null;
55 private Set<String> allKeys = null;
57 protected CaseFormatStore caseFormatStore = null;
58 protected NodeIngestor nodeIngestor;
60 protected Introspector(Object obj) {
61 this.nodeIngestor = SpringContextAware.getBean(NodeIngestor.class);
62 this.caseFormatStore = nodeIngestor.getCaseFormatStore();
65 public abstract boolean hasProperty(String name);
67 protected String convertPropertyName (String name) {
68 return caseFormatStore
69 .fromLowerHyphenToLowerCamel(name)
72 LOGGER.debug("Unable to find {} in the store from lower hyphen to lower camel", name);
73 return CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, name);
78 protected abstract Object get(String name);
79 protected abstract void set(String name, Object value);
82 * @param name the property name you'd like to retrieve the value for
83 * @return the value of the property
85 public <T> T getValue(String name) {
86 String convertedName = convertPropertyName(name);
89 if (this.hasProperty(name)) {
90 result = this.get(convertedName);
92 /* property not found - slightly ambiguous */
96 Class<?> clazz = this.getClass(name);
97 if (this.isListType(name) && result == null) {
99 this.set(convertedName, clazz.newInstance());
100 result = this.get(convertedName);
101 } catch (DynamicException | InstantiationException | IllegalAccessException e) {
102 LOGGER.warn(e.getMessage(),e);
109 public Introspector getWrappedValue(String name) {
110 String convertedName = convertPropertyName(name);
113 if (this.hasProperty(name)) {
114 value = this.get(convertedName);
116 /* property not found - slightly ambiguous */
120 Class<?> clazz = this.getClass(name);
121 if (this.isListType(name) && value == null) {
123 this.set(convertedName, clazz.newInstance());
124 value = this.get(convertedName);
125 } catch (DynamicException | InstantiationException | IllegalAccessException e) {
126 LOGGER.warn(e.getMessage(),e);
130 return IntrospectorFactory.newInstance(this.getModelType(), value);
138 public List<Introspector> getWrappedListValue(String name) {
139 String convertedName = convertPropertyName(name);
141 List<Introspector> resultList = new ArrayList<>();
142 if (this.hasProperty(name)) {
143 value = this.get(convertedName);
145 /* property not found - slightly ambiguous */
148 boolean isListType = this.isListType(name);
149 if (!this.isListType(name)) {
152 Class<?> clazz = this.getClass(name);
153 if (isListType && value == null) {
155 this.set(convertedName, clazz.newInstance());
156 value = this.get(convertedName);
157 } catch (DynamicException | InstantiationException | IllegalAccessException e) {
158 LOGGER.warn(e.getMessage(),e);
162 List<Object> valueList = (List<Object>)value;
164 for (Object item : valueList) {
165 resultList.add(IntrospectorFactory.newInstance(this.getModelType(), item));
172 public Object castValueAccordingToSchema(String name, Object obj) {
174 Class<?> nameClass = this.getClass(name);
175 if (nameClass == null) {
176 throw new IllegalArgumentException("property: " + name + " does not exist on " + this.getDbName());
181 if (!obj.getClass().getName().equals(nameClass.getName())) {
182 if (nameClass.isPrimitive()) {
183 nameClass = ClassUtils.primitiveToWrapper(nameClass);
184 result = nameClass.getConstructor(String.class).newInstance(obj.toString());
186 if (obj instanceof String) {
187 result = nameClass.getConstructor(String.class).newInstance(obj);
188 } else if (!this.isListType(name) && !this.isComplexType(name)){
189 //box = obj.toString();
190 result = nameClass.getConstructor(String.class).newInstance(obj.toString());
193 } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
194 | InvocationTargetException | NoSuchMethodException | SecurityException e) {
195 ErrorLogHelper.logError("AAI_4017", e.getMessage());
201 public List<Object> castValueAccordingToSchema(String name, List<?> objs) {
202 List<Object> result = new ArrayList<>();
204 for (Object item : objs) {
205 result.add(this.castValueAccordingToSchema(name, item));
213 * @param name the property name you'd like to set the value of
214 * @param obj the value to be set
217 public void setValue(String name, Object obj) {
218 Object box = this.castValueAccordingToSchema(name, obj);
220 name = convertPropertyName(name);
225 * @return a list of all the properties available on the object
227 public abstract Set<String> getProperties();
229 public Set<String> getProperties(PropertyPredicate<Introspector, String> p) {
230 final Set<String> temp = new LinkedHashSet<>();
231 this.getProperties().stream().filter(item -> {
232 return p.test(this, item);
236 final Set<String> result = Collections.unmodifiableSet(temp);
242 public Set<String> getSimpleProperties(PropertyPredicate<Introspector, String> p){
243 return this.getProperties()
245 .filter(item -> p.test(this, item))
246 .filter(this::isSimpleType)
247 .collect(Collectors.toSet());
251 * @return a list of the required properties on the object
253 public abstract Set<String> getRequiredProperties();
256 * @return a list of the properties that can be used to query the object in the db
258 public abstract Set<String> getKeys();
261 * @return a list of the all key properties for this object
263 public Set<String> getAllKeys() {
264 Set<String> result = null;
265 if (this.allKeys == null) {
266 Set<String> keys = this.getKeys();
267 result = new LinkedHashSet<>();
269 String altKeys = this.getMetadata(ObjectMetadata.ALTERNATE_KEYS_1);
270 if (altKeys != null) {
271 String[] altKeysArray = altKeys.split(",");
272 for (String altKey : altKeysArray) {
276 result = Collections.unmodifiableSet(result);
277 this.allKeys = result;
279 result = this.allKeys;
283 public Set<String> getIndexedProperties() {
284 Set<String> result = null;
286 if (this.indexedProperties == null) {
287 result = new LinkedHashSet<>();
288 Set<String> keys = this.getKeys();
290 String altKeys = this.getMetadata(ObjectMetadata.INDEXED_PROPS);
291 if (altKeys != null) {
292 String[] altKeysArray = altKeys.split(",");
293 for (String altKey : altKeysArray) {
297 this.indexedProperties = Collections.unmodifiableSet(result);
299 result = this.indexedProperties;
303 public Set<String> getUniqueProperties() {
304 Set<String> result = null;
305 if (this.uniqueProperties == null) {
306 String altKeys = this.getMetadata(ObjectMetadata.UNIQUE_PROPS);
307 result = new LinkedHashSet<>();
308 if (altKeys != null) {
309 String[] altKeysArray = altKeys.split(",");
310 for (String altKey : altKeysArray) {
314 this.uniqueProperties = Collections.unmodifiableSet(result);
317 result = this.uniqueProperties;
321 public Set<String> getDependentOn() {
322 String dependentOn = this.getMetadata(ObjectMetadata.DEPENDENT_ON);
323 if (dependentOn == null) {
324 return new LinkedHashSet<>();
326 return new LinkedHashSet<>(Arrays.asList(dependentOn.split(",")));
331 * @return the string name of the java class of the named property
333 public String getType(String name) {
334 Class<?> resultClass = this.getClass(name);
337 if (resultClass != null) {
338 result = resultClass.getName();
339 if (result.equals("java.util.ArrayList")) {
340 result = "java.util.List";
347 * This will returned the generic parameterized type of the underlying
348 * object if it exists
350 * @return the generic type of the java class of the underlying object
352 public String getGenericType(String name) {
353 Class<?> resultClass = this.getGenericTypeClass(name);
356 if (resultClass != null) {
357 result = resultClass.getName();
364 * @return the string name of the java class of the underlying object
366 public abstract String getJavaClassName();
370 * @param name the property name
371 * @return the Class object
373 public abstract Class<?> getClass(String name);
375 public abstract Class<?> getGenericTypeClass(String name);
379 * @param name the property name
380 * @return a new instance of the underlying type of this property
381 * @throws AAIUnknownObjectException
383 public Object newInstanceOfProperty(String name) throws AAIUnknownObjectException {
384 String type = this.getType(name);
385 return loader.objectFromName(type);
388 public Object newInstanceOfNestedProperty(String name) throws AAIUnknownObjectException {
389 String type = this.getGenericType(name);
390 return loader.objectFromName(type);
394 public Introspector newIntrospectorInstanceOfProperty(String name) throws AAIUnknownObjectException {
396 Introspector result = IntrospectorFactory.newInstance(this.getModelType(), this.newInstanceOfProperty(name));
402 public Introspector newIntrospectorInstanceOfNestedProperty(String name) throws AAIUnknownObjectException {
404 Introspector result = IntrospectorFactory.newInstance(this.getModelType(), this.newInstanceOfNestedProperty(name));
410 * Is this type not a Java String or primitive
414 public boolean isComplexType(String name) {
415 String result = this.getType(name);
417 if (result.contains("aai") || result.equals("java.lang.Object")) {
424 public boolean isComplexGenericType(String name) {
425 String result = this.getGenericType(name);
427 if (result.contains("aai")) {
434 public boolean isSimpleType(String name) {
435 return !(this.isComplexType(name) || this.isListType(name));
438 public boolean isSimpleGenericType(String name) {
439 return !this.isComplexGenericType(name);
442 public boolean isListType(String name) {
443 String result = this.getType(name);
445 if (result.contains("java.util.List")) {
452 public boolean isContainer() {
453 Set<String> props = this.getProperties();
454 boolean result = false;
455 if (props.size() == 1 && this.isListType(props.iterator().next())) {
462 public abstract String getChildName();
463 public String getChildDBName() {
464 String result = this.getChildName();
466 result = namingException.getDBName(result);
469 public abstract String getName();
471 public String getDbName() {
472 String lowerHyphen = this.getName();
474 lowerHyphen = namingException.getDBName(lowerHyphen);
479 public abstract ModelType getModelType();
481 public boolean hasChild(Introspector child) {
482 boolean result = false;
483 //check all inheriting types for this child
484 if ("true".equals(this.getMetadata(ObjectMetadata.ABSTRACT))) {
485 String[] inheritors = this.getMetadata(ObjectMetadata.INHERITORS).split(",");
486 for (String inheritor : inheritors) {
488 Introspector temp = this.loader.introspectorFromName(inheritor);
489 result = temp.hasProperty(child.getName());
493 } catch (AAIUnknownObjectException e) {
494 LOGGER.warn("Skipping inheritor " + inheritor + " (Unknown Object) " + LogFormatTools.getStackTop(e));
498 result = this.hasProperty(child.getName());
503 public void setURIChain(String uri) {
506 public abstract String getObjectId() throws UnsupportedEncodingException;
508 public String getURI() throws UnsupportedEncodingException {
509 //String result = this.uriChain;
511 String namespace = this.getMetadata(ObjectMetadata.NAMESPACE);
512 String container = this.getMetadata(ObjectMetadata.CONTAINER);
513 if (this.isContainer()) {
514 result += "/" + this.getName();
517 if (container != null) {
518 result += "/" + container;
520 result += "/" + this.getDbName() + "/" + this.findKey();
522 if (namespace != null && !namespace.equals("")) {
523 result = "/" + namespace + result;
531 public String getGenericURI() {
533 if (this.isContainer()) {
534 result += "/" + this.getName();
536 result += "/" + this.getDbName();
537 for (String key : this.getKeys()) {
538 result += "/{" + this.getDbName() + "-" + key + "}";
545 public String getFullGenericURI() {
547 String namespace = this.getMetadata(ObjectMetadata.NAMESPACE);
548 String container = this.getMetadata(ObjectMetadata.CONTAINER);
549 if (this.isContainer()) {
550 result += "/" + this.getName();
554 if (container != null) {
555 result += "/" + container;
557 result += "/" + this.getDbName();
559 for (String key : this.getKeys()) {
560 result += "/{" + this.getDbName() + "-" + key + "}";
562 if (namespace != null && !namespace.equals("")) {
563 result = "/" + namespace + result;
571 public abstract String preProcessKey(String key);
573 protected abstract String findKey() throws UnsupportedEncodingException;
575 public abstract String marshal(MarshallerProperties properties);
577 public abstract Object getUnderlyingObject();
579 public String marshal(boolean formatted) {
580 MarshallerProperties properties =
581 new MarshallerProperties.Builder(MediaType.APPLICATION_JSON_TYPE).formatted(formatted).build();
583 return marshal(properties);
585 public String makeSingular(String word) {
587 String result = word;
588 result = result.replaceAll("(?:([ho])es|s)$", "");
590 if (result.equals("ClassesOfService")) {
591 result = "ClassOfService";
592 } else if (result.equals("CvlanTag")) {
593 result = "CvlanTagEntry";
594 } else if (result.equals("Metadata")) {
595 result = "Metadatum";
600 protected String makePlural(String word) {
601 String result = word;
603 if (result.equals("cvlan-tag-entry")) {
605 } else if (result.equals("class-of-service")) {
606 return "classes-of-service";
607 } else if (result.equals("metadatum")) {
610 result = result.replaceAll("([a-z])$", "$1s");
611 result = result.replaceAll("([hox])s$", "$1es");
613 if (result.equals("classes-of-services")) {
614 result = "classes-of-service";
620 public abstract String getMetadata(ObjectMetadata metadataName);
621 public abstract Map<PropertyMetadata, String> getPropertyMetadata(String propName);
622 public Optional<String> getPropertyMetadata(String propName, PropertyMetadata metadataName) {
623 final String resultValue = this.getPropertyMetadata(propName).getOrDefault(metadataName, "");
624 Optional<String> result = Optional.empty();
626 if (!resultValue.isEmpty()) {
627 result = Optional.of(resultValue);
633 public abstract SchemaVersion getVersion();
634 public Loader getLoader() {
638 public boolean isTopLevel() {
640 return this.getMetadata(ObjectMetadata.NAMESPACE) != null;