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;
39 import java.util.stream.Collectors;
42 public abstract class Introspector implements Cloneable {
44 private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(Introspector.class);
46 protected String className;
47 protected String uriChain = "";
48 protected Loader loader;
49 protected final NamingExceptions namingException = NamingExceptions.getInstance();
50 private Set<String> uniqueProperties = null;
51 private Set<String> indexedProperties = null;
52 private Set<String> allKeys = null;
53 protected Introspector(Object obj) {
56 public abstract boolean hasProperty(String name);
58 protected String convertPropertyName (String name) {
59 return CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, name);
62 protected abstract Object get(String name);
63 protected abstract void set(String name, Object value);
66 * @param name the property name you'd like to retrieve the value for
67 * @return the value of the property
69 public <T> T getValue(String name) {
70 String convertedName = convertPropertyName(name);
73 if (this.hasProperty(name)) {
74 result = this.get(convertedName);
76 /* property not found - slightly ambiguous */
80 Class<?> clazz = this.getClass(name);
81 if (this.isListType(name) && result == null) {
83 this.set(convertedName, clazz.newInstance());
84 result = this.get(convertedName);
85 } catch (DynamicException | InstantiationException | IllegalAccessException e) {
86 LOGGER.warn(e.getMessage(),e);
93 public Introspector getWrappedValue(String name) {
94 String convertedName = convertPropertyName(name);
97 if (this.hasProperty(name)) {
98 value = this.get(convertedName);
100 /* property not found - slightly ambiguous */
104 Class<?> clazz = this.getClass(name);
105 if (this.isListType(name) && value == null) {
107 this.set(convertedName, clazz.newInstance());
108 value = this.get(convertedName);
109 } catch (DynamicException | InstantiationException | IllegalAccessException e) {
110 LOGGER.warn(e.getMessage(),e);
114 return IntrospectorFactory.newInstance(this.getModelType(), value);
122 public List<Introspector> getWrappedListValue(String name) {
123 String convertedName = convertPropertyName(name);
125 List<Introspector> resultList = new ArrayList<>();
126 if (this.hasProperty(name)) {
127 value = this.get(convertedName);
129 /* property not found - slightly ambiguous */
132 boolean isListType = this.isListType(name);
133 if (!this.isListType(name)) {
136 Class<?> clazz = this.getClass(name);
137 if (isListType && value == null) {
139 this.set(convertedName, clazz.newInstance());
140 value = this.get(convertedName);
141 } catch (DynamicException | InstantiationException | IllegalAccessException e) {
142 LOGGER.warn(e.getMessage(),e);
146 List<Object> valueList = (List<Object>)value;
148 for (Object item : valueList) {
149 resultList.add(IntrospectorFactory.newInstance(this.getModelType(), item));
156 public Object castValueAccordingToSchema(String name, Object obj) {
158 Class<?> nameClass = this.getClass(name);
159 if (nameClass == null) {
160 throw new IllegalArgumentException("property: " + name + " does not exist on " + this.getDbName());
165 if (!obj.getClass().getName().equals(nameClass.getName())) {
166 if (nameClass.isPrimitive()) {
167 nameClass = ClassUtils.primitiveToWrapper(nameClass);
168 result = nameClass.getConstructor(String.class).newInstance(obj.toString());
170 if (obj instanceof String) {
171 result = nameClass.getConstructor(String.class).newInstance(obj);
172 } else if (!this.isListType(name) && !this.isComplexType(name)){
173 //box = obj.toString();
174 result = nameClass.getConstructor(String.class).newInstance(obj.toString());
177 } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
178 | InvocationTargetException | NoSuchMethodException | SecurityException e) {
179 ErrorLogHelper.logError("AAI_4017", e.getMessage());
185 public List<Object> castValueAccordingToSchema(String name, List<?> objs) {
186 List<Object> result = new ArrayList<>();
188 for (Object item : objs) {
189 result.add(this.castValueAccordingToSchema(name, item));
197 * @param name the property name you'd like to set the value of
198 * @param obj the value to be set
201 public void setValue(String name, Object obj) throws IllegalArgumentException {
202 Object box = this.castValueAccordingToSchema(name, obj);
204 name = convertPropertyName(name);
209 * @return a list of all the properties available on the object
211 public abstract Set<String> getProperties();
213 public Set<String> getProperties(PropertyPredicate<Introspector, String> p) {
214 final Set<String> temp = new LinkedHashSet<>();
215 this.getProperties().stream().filter(item -> {
216 return p.test(this, item);
220 final Set<String> result = Collections.unmodifiableSet(temp);
226 public Set<String> getSimpleProperties(PropertyPredicate<Introspector, String> p){
227 return this.getProperties()
229 .filter(item -> p.test(this, item))
230 .filter(this::isSimpleType)
231 .collect(Collectors.toSet());
235 * @return a list of the required properties on the object
237 public abstract Set<String> getRequiredProperties();
240 * @return a list of the properties that can be used to query the object in the db
242 public abstract Set<String> getKeys();
245 * @return a list of the all key properties for this object
247 public Set<String> getAllKeys() {
248 Set<String> result = null;
249 if (this.allKeys == null) {
250 Set<String> keys = this.getKeys();
251 result = new LinkedHashSet<>();
253 String altKeys = this.getMetadata(ObjectMetadata.ALTERNATE_KEYS_1);
254 if (altKeys != null) {
255 String[] altKeysArray = altKeys.split(",");
256 for (String altKey : altKeysArray) {
260 result = Collections.unmodifiableSet(result);
261 this.allKeys = result;
263 result = this.allKeys;
267 public Set<String> getIndexedProperties() {
268 Set<String> result = null;
270 if (this.indexedProperties == null) {
271 result = new LinkedHashSet<>();
272 Set<String> keys = this.getKeys();
274 String altKeys = this.getMetadata(ObjectMetadata.INDEXED_PROPS);
275 if (altKeys != null) {
276 String[] altKeysArray = altKeys.split(",");
277 for (String altKey : altKeysArray) {
281 this.indexedProperties = Collections.unmodifiableSet(result);
283 result = this.indexedProperties;
287 public Set<String> getUniqueProperties() {
288 Set<String> result = null;
289 if (this.uniqueProperties == null) {
290 String altKeys = this.getMetadata(ObjectMetadata.UNIQUE_PROPS);
291 result = new LinkedHashSet<>();
292 if (altKeys != null) {
293 String[] altKeysArray = altKeys.split(",");
294 for (String altKey : altKeysArray) {
298 this.uniqueProperties = Collections.unmodifiableSet(result);
301 result = this.uniqueProperties;
305 public Set<String> getDependentOn() {
306 String dependentOn = this.getMetadata(ObjectMetadata.DEPENDENT_ON);
307 if (dependentOn == null) {
308 return new LinkedHashSet<>();
310 return new LinkedHashSet<>(Arrays.asList(dependentOn.split(",")));
315 * @return the string name of the java class of the named property
317 public String getType(String name) {
318 Class<?> resultClass = this.getClass(name);
321 if (resultClass != null) {
322 result = resultClass.getName();
323 if (result.equals("java.util.ArrayList")) {
324 result = "java.util.List";
331 * This will returned the generic parameterized type of the underlying
332 * object if it exists
334 * @return the generic type of the java class of the underlying object
336 public String getGenericType(String name) {
337 Class<?> resultClass = this.getGenericTypeClass(name);
340 if (resultClass != null) {
341 result = resultClass.getName();
348 * @return the string name of the java class of the underlying object
350 public abstract String getJavaClassName();
354 * @param name the property name
355 * @return the Class object
357 public abstract Class<?> getClass(String name);
359 public abstract Class<?> getGenericTypeClass(String name);
363 * @param name the property name
364 * @return a new instance of the underlying type of this property
365 * @throws AAIUnknownObjectException
367 public Object newInstanceOfProperty(String name) throws AAIUnknownObjectException {
368 String type = this.getType(name);
369 return loader.objectFromName(type);
372 public Object newInstanceOfNestedProperty(String name) throws AAIUnknownObjectException {
373 String type = this.getGenericType(name);
374 return loader.objectFromName(type);
378 public Introspector newIntrospectorInstanceOfProperty(String name) throws AAIUnknownObjectException {
380 Introspector result = IntrospectorFactory.newInstance(this.getModelType(), this.newInstanceOfProperty(name));
386 public Introspector newIntrospectorInstanceOfNestedProperty(String name) throws AAIUnknownObjectException {
388 Introspector result = IntrospectorFactory.newInstance(this.getModelType(), this.newInstanceOfNestedProperty(name));
394 * Is this type not a Java String or primitive
398 public boolean isComplexType(String name) {
399 String result = this.getType(name);
401 if (result.contains("aai") || result.equals("java.lang.Object")) {
408 public boolean isComplexGenericType(String name) {
409 String result = this.getGenericType(name);
411 if (result.contains("aai")) {
418 public boolean isSimpleType(String name) {
419 return !(this.isComplexType(name) || this.isListType(name));
422 public boolean isSimpleGenericType(String name) {
423 return !this.isComplexGenericType(name);
426 public boolean isListType(String name) {
427 String result = this.getType(name);
429 if (result.contains("java.util.List")) {
436 public boolean isContainer() {
437 Set<String> props = this.getProperties();
438 boolean result = false;
439 if (props.size() == 1 && this.isListType(props.iterator().next())) {
446 public abstract String getChildName();
447 public String getChildDBName() {
448 String result = this.getChildName();
450 result = namingException.getDBName(result);
453 public abstract String getName();
455 public String getDbName() {
456 String lowerHyphen = this.getName();
458 lowerHyphen = namingException.getDBName(lowerHyphen);
463 public abstract ModelType getModelType();
465 public boolean hasChild(Introspector child) {
466 boolean result = false;
467 //check all inheriting types for this child
468 if ("true".equals(this.getMetadata(ObjectMetadata.ABSTRACT))) {
469 String[] inheritors = this.getMetadata(ObjectMetadata.INHERITORS).split(",");
470 for (String inheritor : inheritors) {
472 Introspector temp = this.loader.introspectorFromName(inheritor);
473 result = temp.hasProperty(child.getName());
477 } catch (AAIUnknownObjectException e) {
478 LOGGER.warn("Skipping inheritor " + inheritor + " (Unknown Object) " + LogFormatTools.getStackTop(e));
482 result = this.hasProperty(child.getName());
487 public void setURIChain(String uri) {
490 public abstract String getObjectId() throws UnsupportedEncodingException;
492 public String getURI() throws UnsupportedEncodingException {
493 //String result = this.uriChain;
495 String namespace = this.getMetadata(ObjectMetadata.NAMESPACE);
496 String container = this.getMetadata(ObjectMetadata.CONTAINER);
497 if (this.isContainer()) {
498 result += "/" + this.getName();
501 if (container != null) {
502 result += "/" + container;
504 result += "/" + this.getDbName() + "/" + this.findKey();
506 if (namespace != null && !namespace.equals("")) {
507 result = "/" + namespace + result;
515 public String getGenericURI() {
517 if (this.isContainer()) {
518 result += "/" + this.getName();
520 result += "/" + this.getDbName();
521 for (String key : this.getKeys()) {
522 result += "/{" + this.getDbName() + "-" + key + "}";
529 public String getFullGenericURI() {
531 String namespace = this.getMetadata(ObjectMetadata.NAMESPACE);
532 String container = this.getMetadata(ObjectMetadata.CONTAINER);
533 if (this.isContainer()) {
534 result += "/" + this.getName();
538 if (container != null) {
539 result += "/" + container;
541 result += "/" + this.getDbName();
543 for (String key : this.getKeys()) {
544 result += "/{" + this.getDbName() + "-" + key + "}";
546 if (namespace != null && !namespace.equals("")) {
547 result = "/" + namespace + result;
555 public abstract String preProcessKey(String key);
557 protected abstract String findKey() throws UnsupportedEncodingException;
559 public abstract String marshal(MarshallerProperties properties);
561 public abstract Object clone();
563 public abstract Object getUnderlyingObject();
565 public String marshal(boolean formatted) {
566 MarshallerProperties properties =
567 new MarshallerProperties.Builder(MediaType.APPLICATION_JSON_TYPE).formatted(formatted).build();
569 return marshal(properties);
571 public String makeSingular(String word) {
573 String result = word;
574 result = result.replaceAll("(?:([ho])es|s)$", "");
576 if (result.equals("ClassesOfService")) {
577 result = "ClassOfService";
578 } else if (result.equals("CvlanTag")) {
579 result = "CvlanTagEntry";
580 } else if (result.equals("Metadata")) {
581 result = "Metadatum";
586 protected String makePlural(String word) {
587 String result = word;
589 if (result.equals("cvlan-tag-entry")) {
591 } else if (result.equals("class-of-service")) {
592 return "classes-of-service";
593 } else if (result.equals("metadatum")) {
596 result = result.replaceAll("([a-z])$", "$1s");
597 result = result.replaceAll("([hox])s$", "$1es");
599 if (result.equals("classes-of-services")) {
600 result = "classes-of-service";
606 public abstract String getMetadata(ObjectMetadata metadataName);
607 public abstract Map<PropertyMetadata, String> getPropertyMetadata(String propName);
608 public Optional<String> getPropertyMetadata(String propName, PropertyMetadata metadataName) {
609 final String resultValue = this.getPropertyMetadata(propName).getOrDefault(metadataName, "");
610 Optional<String> result = Optional.empty();
612 if (!resultValue.isEmpty()) {
613 result = Optional.of(resultValue);
619 public abstract SchemaVersion getVersion();
620 public Loader getLoader() {
624 public boolean isTopLevel() {
626 return this.getMetadata(ObjectMetadata.NAMESPACE) != null;