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 instanceof 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;