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.Arrays;
27 import java.util.Collections;
28 import java.util.LinkedHashSet;
29 import java.util.List;
31 import java.util.Optional;
34 import org.apache.commons.lang.ClassUtils;
35 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;
43 import com.att.eelf.configuration.EELFLogger;
44 import com.att.eelf.configuration.EELFManager;
45 import com.google.common.base.CaseFormat;
47 public abstract class Introspector implements Cloneable {
49 private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(Introspector.class);
51 protected String className;
52 protected String uriChain = "";
53 protected Loader loader;
54 protected final NamingExceptions namingException = NamingExceptions.getInstance();
55 private Set<String> uniqueProperties = null;
56 private Set<String> indexedProperties = null;
57 private Set<String> allKeys = null;
58 protected Introspector(Object obj) {
61 public abstract boolean hasProperty(String name);
63 protected String convertPropertyName (String name) {
64 return CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, name);
67 protected abstract Object get(String name);
68 protected abstract void set(String name, Object value);
71 * @param name the property name you'd like to retrieve the value for
72 * @return the value of the property
74 public <T> T getValue(String name) {
75 String convertedName = convertPropertyName(name);
78 if (this.hasProperty(name)) {
79 result = this.get(convertedName);
81 /* property not found - slightly ambiguous */
85 Class<?> clazz = this.getClass(name);
86 if (this.isListType(name) && result == null) {
88 this.set(convertedName, clazz.newInstance());
89 result = this.get(convertedName);
90 } catch (DynamicException | InstantiationException | IllegalAccessException e) {
98 public Introspector getWrappedValue(String name) {
99 String convertedName = convertPropertyName(name);
102 if (this.hasProperty(name)) {
103 value = this.get(convertedName);
105 /* property not found - slightly ambiguous */
109 Class<?> clazz = this.getClass(name);
110 if (this.isListType(name) && value == null) {
112 this.set(convertedName, clazz.newInstance());
113 value = this.get(convertedName);
114 } catch (DynamicException | InstantiationException | IllegalAccessException e) {
119 return IntrospectorFactory.newInstance(this.getModelType(), value);
127 public List<Introspector> getWrappedListValue(String name) {
128 String convertedName = convertPropertyName(name);
130 List<Introspector> resultList = new ArrayList<>();
131 if (this.hasProperty(name)) {
132 value = this.get(convertedName);
134 /* property not found - slightly ambiguous */
137 boolean isListType = this.isListType(name);
138 if (!this.isListType(name)) {
141 Class<?> clazz = this.getClass(name);
142 if (isListType && value == null) {
144 this.set(convertedName, clazz.newInstance());
145 value = this.get(convertedName);
146 } catch (DynamicException | InstantiationException | IllegalAccessException e) {
151 List<Object> valueList = (List<Object>)value;
153 for (Object item : valueList) {
154 resultList.add(IntrospectorFactory.newInstance(this.getModelType(), item));
161 public Object castValueAccordingToSchema(String name, Object obj) {
163 Class<?> nameClass = this.getClass(name);
164 if (nameClass == null) {
165 throw new IllegalArgumentException("property: " + name + " does not exist on " + this.getDbName());
170 if (!obj.getClass().getName().equals(nameClass.getName())) {
171 if (nameClass.isPrimitive()) {
172 nameClass = ClassUtils.primitiveToWrapper(nameClass);
173 result = nameClass.getConstructor(String.class).newInstance(obj.toString());
175 if (obj instanceof String) {
176 result = nameClass.getConstructor(String.class).newInstance(obj);
177 } else if (!this.isListType(name) && !this.isComplexType(name)){
178 //box = obj.toString();
179 result = nameClass.getConstructor(String.class).newInstance(obj.toString());
182 } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
183 | InvocationTargetException | NoSuchMethodException | SecurityException e) {
184 ErrorLogHelper.logError("AAI_4017", e.getMessage());
190 public List<Object> castValueAccordingToSchema(String name, List<?> objs) {
191 List<Object> result = new ArrayList<>();
193 for (Object item : objs) {
194 result.add(this.castValueAccordingToSchema(name, item));
202 * @param name the property name you'd like to set the value of
203 * @param obj the value to be set
206 public void setValue(String name, Object obj) throws IllegalArgumentException {
207 Object box = this.castValueAccordingToSchema(name, obj);
209 name = convertPropertyName(name);
214 * @return a list of all the properties available on the object
216 public abstract Set<String> getProperties();
218 public Set<String> getProperties(PropertyPredicate<Introspector, String> p) {
219 final Set<String> temp = new LinkedHashSet<>();
220 this.getProperties().stream().filter(item -> {
221 return p.test(this, item);
225 final Set<String> result = Collections.unmodifiableSet(temp);
232 * @return a list of the required properties on the object
234 public abstract Set<String> getRequiredProperties();
237 * @return a list of the properties that can be used to query the object in the db
239 public abstract Set<String> getKeys();
242 * @return a list of the all key properties for this object
244 public Set<String> getAllKeys() {
245 Set<String> result = null;
246 if (this.allKeys == null) {
247 Set<String> keys = this.getKeys();
248 result = new LinkedHashSet<>();
250 String altKeys = this.getMetadata(ObjectMetadata.ALTERNATE_KEYS_1);
251 if (altKeys != null) {
252 String[] altKeysArray = altKeys.split(",");
253 for (String altKey : altKeysArray) {
257 result = Collections.unmodifiableSet(result);
258 this.allKeys = result;
260 result = this.allKeys;
264 public Set<String> getIndexedProperties() {
265 Set<String> result = null;
267 if (this.indexedProperties == null) {
268 result = new LinkedHashSet<>();
269 Set<String> keys = this.getKeys();
271 String altKeys = this.getMetadata(ObjectMetadata.INDEXED_PROPS);
272 if (altKeys != null) {
273 String[] altKeysArray = altKeys.split(",");
274 for (String altKey : altKeysArray) {
278 this.indexedProperties = Collections.unmodifiableSet(result);
280 result = this.indexedProperties;
284 public Set<String> getUniqueProperties() {
285 Set<String> result = null;
286 if (this.uniqueProperties == null) {
287 String altKeys = this.getMetadata(ObjectMetadata.UNIQUE_PROPS);
288 result = new LinkedHashSet<>();
289 if (altKeys != null) {
290 String[] altKeysArray = altKeys.split(",");
291 for (String altKey : altKeysArray) {
295 this.uniqueProperties = Collections.unmodifiableSet(result);
298 result = this.uniqueProperties;
302 public Set<String> getDependentOn() {
303 String dependentOn = this.getMetadata(ObjectMetadata.DEPENDENT_ON);
304 if (dependentOn == null) {
305 return new LinkedHashSet<>();
307 return new LinkedHashSet<>(Arrays.asList(dependentOn.split(",")));
312 * @return the string name of the java class of the named property
314 public String getType(String name) {
315 Class<?> resultClass = this.getClass(name);
318 if (resultClass != null) {
319 result = resultClass.getName();
320 if (result.equals("java.util.ArrayList")) {
321 result = "java.util.List";
328 * This will returned the generic parameterized type of the underlying
329 * object if it exists
331 * @return the generic type of the java class of the underlying object
333 public String getGenericType(String name) {
334 Class<?> resultClass = this.getGenericTypeClass(name);
337 if (resultClass != null) {
338 result = resultClass.getName();
345 * @return the string name of the java class of the underlying object
347 public abstract String getJavaClassName();
351 * @param name the property name
352 * @return the Class object
354 public abstract Class<?> getClass(String name);
356 public abstract Class<?> getGenericTypeClass(String name);
360 * @param name the property name
361 * @return a new instance of the underlying type of this property
362 * @throws AAIUnknownObjectException
364 public Object newInstanceOfProperty(String name) throws AAIUnknownObjectException {
365 String type = this.getType(name);
366 return loader.objectFromName(type);
369 public Object newInstanceOfNestedProperty(String name) throws AAIUnknownObjectException {
370 String type = this.getGenericType(name);
371 return loader.objectFromName(type);
375 public Introspector newIntrospectorInstanceOfProperty(String name) throws AAIUnknownObjectException {
377 Introspector result = IntrospectorFactory.newInstance(this.getModelType(), this.newInstanceOfProperty(name));
383 public Introspector newIntrospectorInstanceOfNestedProperty(String name) throws AAIUnknownObjectException {
385 Introspector result = IntrospectorFactory.newInstance(this.getModelType(), this.newInstanceOfNestedProperty(name));
391 * Is this type not a Java String or primitive
395 public boolean isComplexType(String name) {
396 String result = this.getType(name);
398 if (result.contains("aai") || result.equals("java.lang.Object")) {
405 public boolean isComplexGenericType(String name) {
406 String result = this.getGenericType(name);
408 if (result.contains("aai")) {
415 public boolean isSimpleType(String name) {
416 return !(this.isComplexType(name) || this.isListType(name));
419 public boolean isSimpleGenericType(String name) {
420 return !this.isComplexGenericType(name);
423 public boolean isListType(String name) {
424 String result = this.getType(name);
426 if (result.contains("java.util.List")) {
433 public boolean isContainer() {
434 Set<String> props = this.getProperties();
435 boolean result = false;
436 if (props.size() == 1 && this.isListType(props.iterator().next())) {
443 public abstract String getChildName();
444 public String getChildDBName() {
445 String result = this.getChildName();
447 result = namingException.getDBName(result);
450 public abstract String getName();
452 public String getDbName() {
453 String lowerHyphen = this.getName();
455 lowerHyphen = namingException.getDBName(lowerHyphen);
460 public abstract ModelType getModelType();
462 public boolean hasChild(Introspector child) {
463 boolean result = false;
464 //check all inheriting types for this child
465 if ("true".equals(this.getMetadata(ObjectMetadata.ABSTRACT))) {
466 String[] inheritors = this.getMetadata(ObjectMetadata.INHERITORS).split(",");
467 for (String inheritor : inheritors) {
469 Introspector temp = this.loader.introspectorFromName(inheritor);
470 result = temp.hasProperty(child.getName());
474 } catch (AAIUnknownObjectException e) {
475 LOGGER.warn("Skipping inheritor " + inheritor + " (Unknown Object)", e);
479 result = this.hasProperty(child.getName());
484 public void setURIChain(String uri) {
487 public abstract String getObjectId() throws UnsupportedEncodingException;
489 public String getURI() throws UnsupportedEncodingException {
490 //String result = this.uriChain;
492 String namespace = this.getMetadata(ObjectMetadata.NAMESPACE);
493 String container = this.getMetadata(ObjectMetadata.CONTAINER);
494 if (this.isContainer()) {
495 result += "/" + this.getName();
498 if (container != null) {
499 result += "/" + container;
501 result += "/" + this.getDbName() + "/" + this.findKey();
503 if (namespace != null && !namespace.equals("")) {
504 result = "/" + namespace + result;
512 public String getGenericURI() {
514 if (this.isContainer()) {
515 result += "/" + this.getName();
517 result += "/" + this.getDbName();
518 for (String key : this.getKeys()) {
519 result += "/{" + this.getDbName() + "-" + key + "}";
526 public String getFullGenericURI() {
528 String namespace = this.getMetadata(ObjectMetadata.NAMESPACE);
529 String container = this.getMetadata(ObjectMetadata.CONTAINER);
530 if (this.isContainer()) {
531 result += "/" + this.getName();
535 if (container != null) {
536 result += "/" + container;
538 result += "/" + this.getDbName();
540 for (String key : this.getKeys()) {
541 result += "/{" + this.getDbName() + "-" + key + "}";
543 if (namespace != null && !namespace.equals("")) {
544 result = "/" + namespace + result;
552 public abstract String preProcessKey(String key);
554 protected abstract String findKey() throws UnsupportedEncodingException;
556 public abstract String marshal(MarshallerProperties properties);
558 public abstract Object clone();
560 public abstract Object getUnderlyingObject();
562 public String marshal(boolean formatted) {
563 MarshallerProperties properties =
564 new MarshallerProperties.Builder(MediaType.APPLICATION_JSON_TYPE).formatted(formatted).build();
566 return marshal(properties);
568 public String makeSingular(String word) {
570 String result = word;
571 result = result.replaceAll("(?:([ho])es|s)$", "");
573 if (result.equals("ClassesOfService")) {
574 result = "ClassOfService";
575 } else if (result.equals("CvlanTag")) {
576 result = "CvlanTagEntry";
577 } else if (result.equals("Metadata")) {
578 result = "Metadatum";
583 protected String makePlural(String word) {
584 String result = word;
586 if (result.equals("cvlan-tag-entry")) {
588 } else if (result.equals("class-of-service")) {
589 return "classes-of-service";
590 } else if (result.equals("metadatum")) {
593 result = result.replaceAll("([a-z])$", "$1s");
594 result = result.replaceAll("([hox])s$", "$1es");
596 if (result.equals("classes-of-services")) {
597 result = "classes-of-service";
603 public abstract String getMetadata(ObjectMetadata metadataName);
604 public abstract Map<PropertyMetadata, String> getPropertyMetadata(String propName);
605 public Optional<String> getPropertyMetadata(String propName, PropertyMetadata metadataName) {
606 final String resultValue = this.getPropertyMetadata(propName).getOrDefault(metadataName, "");
607 Optional<String> result = Optional.empty();
609 if (!resultValue.isEmpty()) {
610 result = Optional.of(resultValue);
616 public abstract Version getVersion();
617 public Loader getLoader() {
621 public boolean isTopLevel() {
623 return this.getMetadata(ObjectMetadata.NAMESPACE) != null;