2 * ============LICENSE_START=======================================================
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
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=========================================================
21 package org.openecomp.aai.introspection;
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;
30 import java.util.Optional;
33 import org.apache.commons.lang.ClassUtils;
34 import org.eclipse.persistence.exceptions.DynamicException;
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;
46 public abstract class Introspector implements Cloneable {
48 private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(Introspector.class);
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) {
60 public abstract boolean hasProperty(String name);
62 protected String convertPropertyName (String name) {
63 return CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, name);
66 protected abstract Object get(String name);
67 protected abstract void set(String name, Object value);
70 * @param name the property name you'd like to retrieve the value for
71 * @return the value of the property
73 public <T> T getValue(String name) {
74 String convertedName = convertPropertyName(name);
77 if (this.hasProperty(name)) {
78 result = this.get(convertedName);
80 /* property not found - slightly ambiguous */
84 Class<?> clazz = this.getClass(name);
85 if (this.isListType(name) && result == null) {
87 this.set(convertedName, clazz.newInstance());
88 result = this.get(convertedName);
89 } catch (DynamicException | InstantiationException | IllegalAccessException e) {
97 public Introspector getWrappedValue(String name) {
98 String convertedName = convertPropertyName(name);
101 if (this.hasProperty(name)) {
102 value = this.get(convertedName);
104 /* property not found - slightly ambiguous */
108 Class<?> clazz = this.getClass(name);
109 if (this.isListType(name) && value == null) {
111 this.set(convertedName, clazz.newInstance());
112 value = this.get(convertedName);
113 } catch (DynamicException | InstantiationException | IllegalAccessException e) {
118 return IntrospectorFactory.newInstance(this.getModelType(), value);
126 public List<Introspector> getWrappedListValue(String name) {
127 String convertedName = convertPropertyName(name);
129 List<Introspector> resultList = new ArrayList<>();
130 if (this.hasProperty(name)) {
131 value = this.get(convertedName);
133 /* property not found - slightly ambiguous */
136 boolean isListType = this.isListType(name);
137 if (!this.isListType(name)) {
140 Class<?> clazz = this.getClass(name);
141 if (isListType && value == null) {
143 this.set(convertedName, clazz.newInstance());
144 value = this.get(convertedName);
145 } catch (DynamicException | InstantiationException | IllegalAccessException e) {
150 List<Object> valueList = (List<Object>)value;
152 for (Object item : valueList) {
153 resultList.add(IntrospectorFactory.newInstance(this.getModelType(), item));
160 public Object castValueAccordingToSchema(String name, Object obj) {
162 Class<?> nameClass = this.getClass(name);
163 if (nameClass == null) {
164 throw new IllegalArgumentException("property: " + name + " does not exist on " + this.getDbName());
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());
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());
181 } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
182 | InvocationTargetException | NoSuchMethodException | SecurityException e) {
183 ErrorLogHelper.logError("AAI_4017", e.getMessage());
189 public List<Object> castValueAccordingToSchema(String name, List<?> objs) {
190 List<Object> result = new ArrayList<>();
192 for (Object item : objs) {
193 result.add(this.castValueAccordingToSchema(name, item));
201 * @param name the property name you'd like to set the value of
202 * @param obj the value to be set
205 public void setValue(String name, Object obj) throws IllegalArgumentException {
206 Object box = this.castValueAccordingToSchema(name, obj);
208 name = convertPropertyName(name);
213 * @return a list of all the properties available on the object
215 public abstract Set<String> getProperties();
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);
224 final Set<String> result = Collections.unmodifiableSet(temp);
231 * @return a list of the required properties on the object
233 public abstract Set<String> getRequiredProperties();
236 * @return a list of the properties that can be used to query the object in the db
238 public abstract Set<String> getKeys();
241 * @return a list of the all key properties for this object
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<>();
249 String altKeys = this.getMetadata(ObjectMetadata.ALTERNATE_KEYS_1);
250 if (altKeys != null) {
251 String[] altKeysArray = altKeys.split(",");
252 for (String altKey : altKeysArray) {
256 result = Collections.unmodifiableSet(result);
257 this.allKeys = result;
259 result = this.allKeys;
263 public Set<String> getIndexedProperties() {
264 Set<String> result = null;
266 if (this.indexedProperties == null) {
267 result = new LinkedHashSet<>();
268 Set<String> keys = this.getKeys();
270 String altKeys = this.getMetadata(ObjectMetadata.INDEXED_PROPS);
271 if (altKeys != null) {
272 String[] altKeysArray = altKeys.split(",");
273 for (String altKey : altKeysArray) {
277 this.indexedProperties = Collections.unmodifiableSet(result);
279 result = this.indexedProperties;
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) {
294 this.uniqueProperties = Collections.unmodifiableSet(result);
297 result = this.uniqueProperties;
303 * @return the string name of the java class of the named property
305 public String getType(String name) {
306 Class<?> resultClass = this.getClass(name);
309 if (resultClass != null) {
310 result = resultClass.getName();
311 if (result.equals("java.util.ArrayList")) {
312 result = "java.util.List";
319 * This will returned the generic parameterized type of the underlying
320 * object if it exists
322 * @return the generic type of the java class of the underlying object
324 public String getGenericType(String name) {
325 Class<?> resultClass = this.getGenericTypeClass(name);
328 if (resultClass != null) {
329 result = resultClass.getName();
336 * @return the string name of the java class of the underlying object
338 public abstract String getJavaClassName();
342 * @param name the property name
343 * @return the Class object
345 public abstract Class<?> getClass(String name);
347 public abstract Class<?> getGenericTypeClass(String name);
351 * @param name the property name
352 * @return a new instance of the underlying type of this property
353 * @throws AAIUnknownObjectException
355 public Object newInstanceOfProperty(String name) throws AAIUnknownObjectException {
356 String type = this.getType(name);
357 return loader.objectFromName(type);
360 public Object newInstanceOfNestedProperty(String name) throws AAIUnknownObjectException {
361 String type = this.getGenericType(name);
362 return loader.objectFromName(type);
366 public Introspector newIntrospectorInstanceOfProperty(String name) throws AAIUnknownObjectException {
368 Introspector result = IntrospectorFactory.newInstance(this.getModelType(), this.newInstanceOfProperty(name));
374 public Introspector newIntrospectorInstanceOfNestedProperty(String name) throws AAIUnknownObjectException {
376 Introspector result = IntrospectorFactory.newInstance(this.getModelType(), this.newInstanceOfNestedProperty(name));
382 * Is this type not a Java String or primitive
386 public boolean isComplexType(String name) {
387 String result = this.getType(name);
389 if (result.contains("aai") || result.equals("java.lang.Object")) {
396 public boolean isComplexGenericType(String name) {
397 String result = this.getGenericType(name);
399 if (result.contains("aai")) {
406 public boolean isSimpleType(String name) {
407 return !(this.isComplexType(name) || this.isListType(name));
410 public boolean isSimpleGenericType(String name) {
411 return !this.isComplexGenericType(name);
414 public boolean isListType(String name) {
415 String result = this.getType(name);
417 if (result.contains("java.util.List")) {
424 public boolean isContainer() {
425 Set<String> props = this.getProperties();
426 boolean result = false;
427 if (props.size() == 1 && this.isListType(props.iterator().next())) {
434 public abstract String getChildName();
435 public String getChildDBName() {
436 String result = this.getChildName();
438 result = namingException.getDBName(result);
441 public abstract String getName();
443 public String getDbName() {
444 String lowerHyphen = this.getName();
446 lowerHyphen = namingException.getDBName(lowerHyphen);
451 public abstract ModelType getModelType();
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) {
460 Introspector temp = this.loader.introspectorFromName(inheritor);
461 result = temp.hasProperty(child.getName());
465 } catch (AAIUnknownObjectException e) {
466 LOGGER.warn("Skipping inheritor " + inheritor + " (Unknown Object)", e);
470 result = this.hasProperty(child.getName());
475 public void setURIChain(String uri) {
478 public abstract String getObjectId() throws UnsupportedEncodingException;
480 public String getURI() throws UnsupportedEncodingException {
481 //String result = this.uriChain;
483 String namespace = this.getMetadata(ObjectMetadata.NAMESPACE);
484 String container = this.getMetadata(ObjectMetadata.CONTAINER);
485 if (this.isContainer()) {
486 result += "/" + this.getName();
489 if (container != null) {
490 result += "/" + container;
492 result += "/" + this.getDbName() + "/" + this.findKey();
494 if (namespace != null && !namespace.equals("")) {
495 result = "/" + namespace + result;
503 public String getGenericURI() {
505 if (this.isContainer()) {
506 result += "/" + this.getName();
508 result += "/" + this.getDbName();
509 for (String key : this.getKeys()) {
510 result += "/{" + this.getDbName() + "-" + key + "}";
517 public String getFullGenericURI() {
519 String namespace = this.getMetadata(ObjectMetadata.NAMESPACE);
520 String container = this.getMetadata(ObjectMetadata.CONTAINER);
521 if (this.isContainer()) {
522 result += "/" + this.getName();
526 if (container != null) {
527 result += "/" + container;
529 result += "/" + this.getDbName();
531 for (String key : this.getKeys()) {
532 result += "/{" + this.getDbName() + "-" + key + "}";
534 if (namespace != null && !namespace.equals("")) {
535 result = "/" + namespace + result;
543 public abstract String preProcessKey(String key);
545 protected abstract String findKey() throws UnsupportedEncodingException;
547 public abstract String marshal(MarshallerProperties properties);
549 public abstract Object clone();
551 public abstract Object getUnderlyingObject();
553 public String marshal(boolean formatted) {
554 MarshallerProperties properties =
555 new MarshallerProperties.Builder(MediaType.APPLICATION_JSON_TYPE).formatted(formatted).build();
557 return marshal(properties);
559 public String makeSingular(String word) {
561 String result = word;
562 result = result.replaceAll("(?:([ho])es|s)$", "");
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";
574 protected String makePlural(String word) {
575 String result = word;
577 if (result.equals("cvlan-tag-entry")) {
579 } else if (result.equals("class-of-service")) {
580 return "classes-of-service";
581 } else if (result.equals("metadatum")) {
584 result = result.replaceAll("([a-z])$", "$1s");
585 result = result.replaceAll("([hox])s$", "$1es");
587 if (result.equals("classes-of-services")) {
588 result = "classes-of-service";
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();
600 if (!resultValue.isEmpty()) {
601 result = Optional.of(resultValue);
607 public abstract Version getVersion();
608 public Loader getLoader() {
612 public boolean isTopLevel() {
614 return this.getMetadata(ObjectMetadata.NAMESPACE) != null;