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.setup.SchemaVersion;
34 import org.onap.aai.workarounds.NamingExceptions;
36 import java.io.UnsupportedEncodingException;
37 import java.lang.reflect.InvocationTargetException;
41 public abstract class Introspector implements Cloneable {
43 private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(Introspector.class);
45 protected String className;
46 protected String uriChain = "";
47 protected Loader loader;
48 protected final NamingExceptions namingException = NamingExceptions.getInstance();
49 private Set<String> uniqueProperties = null;
50 private Set<String> indexedProperties = null;
51 private Set<String> allKeys = null;
52 protected Introspector(Object obj) {
55 public abstract boolean hasProperty(String name);
57 protected String convertPropertyName (String name) {
58 return CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, name);
61 protected abstract Object get(String name);
62 protected abstract void set(String name, Object value);
65 * @param name the property name you'd like to retrieve the value for
66 * @return the value of the property
68 public <T> T getValue(String name) {
69 String convertedName = convertPropertyName(name);
72 if (this.hasProperty(name)) {
73 result = this.get(convertedName);
75 /* property not found - slightly ambiguous */
79 Class<?> clazz = this.getClass(name);
80 if (this.isListType(name) && result == null) {
82 this.set(convertedName, clazz.newInstance());
83 result = this.get(convertedName);
84 } catch (DynamicException | InstantiationException | IllegalAccessException e) {
85 LOGGER.warn(e.getMessage(),e);
92 public Introspector getWrappedValue(String name) {
93 String convertedName = convertPropertyName(name);
96 if (this.hasProperty(name)) {
97 value = this.get(convertedName);
99 /* property not found - slightly ambiguous */
103 Class<?> clazz = this.getClass(name);
104 if (this.isListType(name) && value == null) {
106 this.set(convertedName, clazz.newInstance());
107 value = this.get(convertedName);
108 } catch (DynamicException | InstantiationException | IllegalAccessException e) {
109 LOGGER.warn(e.getMessage(),e);
113 return IntrospectorFactory.newInstance(this.getModelType(), value);
121 public List<Introspector> getWrappedListValue(String name) {
122 String convertedName = convertPropertyName(name);
124 List<Introspector> resultList = new ArrayList<>();
125 if (this.hasProperty(name)) {
126 value = this.get(convertedName);
128 /* property not found - slightly ambiguous */
131 boolean isListType = this.isListType(name);
132 if (!this.isListType(name)) {
135 Class<?> clazz = this.getClass(name);
136 if (isListType && value == null) {
138 this.set(convertedName, clazz.newInstance());
139 value = this.get(convertedName);
140 } catch (DynamicException | InstantiationException | IllegalAccessException e) {
141 LOGGER.warn(e.getMessage(),e);
145 List<Object> valueList = (List<Object>)value;
147 for (Object item : valueList) {
148 resultList.add(IntrospectorFactory.newInstance(this.getModelType(), item));
155 public Object castValueAccordingToSchema(String name, Object obj) {
157 Class<?> nameClass = this.getClass(name);
158 if (nameClass == null) {
159 throw new IllegalArgumentException("property: " + name + " does not exist on " + this.getDbName());
164 if (!obj.getClass().getName().equals(nameClass.getName())) {
165 if (nameClass.isPrimitive()) {
166 nameClass = ClassUtils.primitiveToWrapper(nameClass);
167 result = nameClass.getConstructor(String.class).newInstance(obj.toString());
169 if (obj instanceof String) {
170 result = nameClass.getConstructor(String.class).newInstance(obj);
171 } else if (!this.isListType(name) && !this.isComplexType(name)){
172 //box = obj.toString();
173 result = nameClass.getConstructor(String.class).newInstance(obj.toString());
176 } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
177 | InvocationTargetException | NoSuchMethodException | SecurityException e) {
178 ErrorLogHelper.logError("AAI_4017", e.getMessage());
184 public List<Object> castValueAccordingToSchema(String name, List<?> objs) {
185 List<Object> result = new ArrayList<>();
187 for (Object item : objs) {
188 result.add(this.castValueAccordingToSchema(name, item));
196 * @param name the property name you'd like to set the value of
197 * @param obj the value to be set
200 public void setValue(String name, Object obj) throws IllegalArgumentException {
201 Object box = this.castValueAccordingToSchema(name, obj);
203 name = convertPropertyName(name);
208 * @return a list of all the properties available on the object
210 public abstract Set<String> getProperties();
212 public Set<String> getProperties(PropertyPredicate<Introspector, String> p) {
213 final Set<String> temp = new LinkedHashSet<>();
214 this.getProperties().stream().filter(item -> {
215 return p.test(this, item);
219 final Set<String> result = Collections.unmodifiableSet(temp);
226 * @return a list of the required properties on the object
228 public abstract Set<String> getRequiredProperties();
231 * @return a list of the properties that can be used to query the object in the db
233 public abstract Set<String> getKeys();
236 * @return a list of the all key properties for this object
238 public Set<String> getAllKeys() {
239 Set<String> result = null;
240 if (this.allKeys == null) {
241 Set<String> keys = this.getKeys();
242 result = new LinkedHashSet<>();
244 String altKeys = this.getMetadata(ObjectMetadata.ALTERNATE_KEYS_1);
245 if (altKeys != null) {
246 String[] altKeysArray = altKeys.split(",");
247 for (String altKey : altKeysArray) {
251 result = Collections.unmodifiableSet(result);
252 this.allKeys = result;
254 result = this.allKeys;
258 public Set<String> getIndexedProperties() {
259 Set<String> result = null;
261 if (this.indexedProperties == null) {
262 result = new LinkedHashSet<>();
263 Set<String> keys = this.getKeys();
265 String altKeys = this.getMetadata(ObjectMetadata.INDEXED_PROPS);
266 if (altKeys != null) {
267 String[] altKeysArray = altKeys.split(",");
268 for (String altKey : altKeysArray) {
272 this.indexedProperties = Collections.unmodifiableSet(result);
274 result = this.indexedProperties;
278 public Set<String> getUniqueProperties() {
279 Set<String> result = null;
280 if (this.uniqueProperties == null) {
281 String altKeys = this.getMetadata(ObjectMetadata.UNIQUE_PROPS);
282 result = new LinkedHashSet<>();
283 if (altKeys != null) {
284 String[] altKeysArray = altKeys.split(",");
285 for (String altKey : altKeysArray) {
289 this.uniqueProperties = Collections.unmodifiableSet(result);
292 result = this.uniqueProperties;
296 public Set<String> getDependentOn() {
297 String dependentOn = this.getMetadata(ObjectMetadata.DEPENDENT_ON);
298 if (dependentOn == null) {
299 return new LinkedHashSet<>();
301 return new LinkedHashSet<>(Arrays.asList(dependentOn.split(",")));
306 * @return the string name of the java class of the named property
308 public String getType(String name) {
309 Class<?> resultClass = this.getClass(name);
312 if (resultClass != null) {
313 result = resultClass.getName();
314 if (result.equals("java.util.ArrayList")) {
315 result = "java.util.List";
322 * This will returned the generic parameterized type of the underlying
323 * object if it exists
325 * @return the generic type of the java class of the underlying object
327 public String getGenericType(String name) {
328 Class<?> resultClass = this.getGenericTypeClass(name);
331 if (resultClass != null) {
332 result = resultClass.getName();
339 * @return the string name of the java class of the underlying object
341 public abstract String getJavaClassName();
345 * @param name the property name
346 * @return the Class object
348 public abstract Class<?> getClass(String name);
350 public abstract Class<?> getGenericTypeClass(String name);
354 * @param name the property name
355 * @return a new instance of the underlying type of this property
356 * @throws AAIUnknownObjectException
358 public Object newInstanceOfProperty(String name) throws AAIUnknownObjectException {
359 String type = this.getType(name);
360 return loader.objectFromName(type);
363 public Object newInstanceOfNestedProperty(String name) throws AAIUnknownObjectException {
364 String type = this.getGenericType(name);
365 return loader.objectFromName(type);
369 public Introspector newIntrospectorInstanceOfProperty(String name) throws AAIUnknownObjectException {
371 Introspector result = IntrospectorFactory.newInstance(this.getModelType(), this.newInstanceOfProperty(name));
377 public Introspector newIntrospectorInstanceOfNestedProperty(String name) throws AAIUnknownObjectException {
379 Introspector result = IntrospectorFactory.newInstance(this.getModelType(), this.newInstanceOfNestedProperty(name));
385 * Is this type not a Java String or primitive
389 public boolean isComplexType(String name) {
390 String result = this.getType(name);
392 if (result.contains("aai") || result.equals("java.lang.Object")) {
399 public boolean isComplexGenericType(String name) {
400 String result = this.getGenericType(name);
402 if (result.contains("aai")) {
409 public boolean isSimpleType(String name) {
410 return !(this.isComplexType(name) || this.isListType(name));
413 public boolean isSimpleGenericType(String name) {
414 return !this.isComplexGenericType(name);
417 public boolean isListType(String name) {
418 String result = this.getType(name);
420 if (result.contains("java.util.List")) {
427 public boolean isContainer() {
428 Set<String> props = this.getProperties();
429 boolean result = false;
430 if (props.size() == 1 && this.isListType(props.iterator().next())) {
437 public abstract String getChildName();
438 public String getChildDBName() {
439 String result = this.getChildName();
441 result = namingException.getDBName(result);
444 public abstract String getName();
446 public String getDbName() {
447 String lowerHyphen = this.getName();
449 lowerHyphen = namingException.getDBName(lowerHyphen);
454 public abstract ModelType getModelType();
456 public boolean hasChild(Introspector child) {
457 boolean result = false;
458 //check all inheriting types for this child
459 if ("true".equals(this.getMetadata(ObjectMetadata.ABSTRACT))) {
460 String[] inheritors = this.getMetadata(ObjectMetadata.INHERITORS).split(",");
461 for (String inheritor : inheritors) {
463 Introspector temp = this.loader.introspectorFromName(inheritor);
464 result = temp.hasProperty(child.getName());
468 } catch (AAIUnknownObjectException e) {
469 LOGGER.warn("Skipping inheritor " + inheritor + " (Unknown Object) " + LogFormatTools.getStackTop(e));
473 result = this.hasProperty(child.getName());
478 public void setURIChain(String uri) {
481 public abstract String getObjectId() throws UnsupportedEncodingException;
483 public String getURI() throws UnsupportedEncodingException {
484 //String result = this.uriChain;
486 String namespace = this.getMetadata(ObjectMetadata.NAMESPACE);
487 String container = this.getMetadata(ObjectMetadata.CONTAINER);
488 if (this.isContainer()) {
489 result += "/" + this.getName();
492 if (container != null) {
493 result += "/" + container;
495 result += "/" + this.getDbName() + "/" + this.findKey();
497 if (namespace != null && !namespace.equals("")) {
498 result = "/" + namespace + result;
506 public String getGenericURI() {
508 if (this.isContainer()) {
509 result += "/" + this.getName();
511 result += "/" + this.getDbName();
512 for (String key : this.getKeys()) {
513 result += "/{" + this.getDbName() + "-" + key + "}";
520 public String getFullGenericURI() {
522 String namespace = this.getMetadata(ObjectMetadata.NAMESPACE);
523 String container = this.getMetadata(ObjectMetadata.CONTAINER);
524 if (this.isContainer()) {
525 result += "/" + this.getName();
529 if (container != null) {
530 result += "/" + container;
532 result += "/" + this.getDbName();
534 for (String key : this.getKeys()) {
535 result += "/{" + this.getDbName() + "-" + key + "}";
537 if (namespace != null && !namespace.equals("")) {
538 result = "/" + namespace + result;
546 public abstract String preProcessKey(String key);
548 protected abstract String findKey() throws UnsupportedEncodingException;
550 public abstract String marshal(MarshallerProperties properties);
552 public abstract Object clone();
554 public abstract Object getUnderlyingObject();
556 public String marshal(boolean formatted) {
557 MarshallerProperties properties =
558 new MarshallerProperties.Builder(MediaType.APPLICATION_JSON_TYPE).formatted(formatted).build();
560 return marshal(properties);
562 public String makeSingular(String word) {
564 String result = word;
565 result = result.replaceAll("(?:([ho])es|s)$", "");
567 if (result.equals("ClassesOfService")) {
568 result = "ClassOfService";
569 } else if (result.equals("CvlanTag")) {
570 result = "CvlanTagEntry";
571 } else if (result.equals("Metadata")) {
572 result = "Metadatum";
577 protected String makePlural(String word) {
578 String result = word;
580 if (result.equals("cvlan-tag-entry")) {
582 } else if (result.equals("class-of-service")) {
583 return "classes-of-service";
584 } else if (result.equals("metadatum")) {
587 result = result.replaceAll("([a-z])$", "$1s");
588 result = result.replaceAll("([hox])s$", "$1es");
590 if (result.equals("classes-of-services")) {
591 result = "classes-of-service";
597 public abstract String getMetadata(ObjectMetadata metadataName);
598 public abstract Map<PropertyMetadata, String> getPropertyMetadata(String propName);
599 public Optional<String> getPropertyMetadata(String propName, PropertyMetadata metadataName) {
600 final String resultValue = this.getPropertyMetadata(propName).getOrDefault(metadataName, "");
601 Optional<String> result = Optional.empty();
603 if (!resultValue.isEmpty()) {
604 result = Optional.of(resultValue);
610 public abstract SchemaVersion getVersion();
611 public Loader getLoader() {
615 public boolean isTopLevel() {
617 return this.getMetadata(ObjectMetadata.NAMESPACE) != null;