2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright © 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=========================================================
20 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22 package org.onap.aai.introspection;
24 import com.att.eelf.configuration.EELFLogger;
25 import com.att.eelf.configuration.EELFManager;
26 import com.google.common.base.CaseFormat;
27 import org.apache.commons.lang.ClassUtils;
28 import org.eclipse.persistence.exceptions.DynamicException;
29 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
30 import org.onap.aai.logging.ErrorLogHelper;
31 import org.onap.aai.restcore.MediaType;
32 import org.onap.aai.schema.enums.ObjectMetadata;
33 import org.onap.aai.schema.enums.PropertyMetadata;
34 import org.onap.aai.workarounds.NamingExceptions;
36 import java.io.UnsupportedEncodingException;
37 import java.lang.reflect.InvocationTargetException;
40 public abstract class Introspector implements Cloneable {
42 private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(Introspector.class);
44 protected String className;
45 protected String uriChain = "";
46 protected Loader loader;
47 protected final NamingExceptions namingException = NamingExceptions.getInstance();
48 private Set<String> uniqueProperties = null;
49 private Set<String> indexedProperties = null;
50 private Set<String> allKeys = null;
51 protected Introspector(Object obj) {
54 public abstract boolean hasProperty(String name);
56 protected String convertPropertyName (String name) {
57 return CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, name);
60 protected abstract Object get(String name);
61 protected abstract void set(String name, Object value);
64 * @param name the property name you'd like to retrieve the value for
65 * @return the value of the property
67 public <T> T getValue(String name) {
68 String convertedName = convertPropertyName(name);
71 if (this.hasProperty(name)) {
72 result = this.get(convertedName);
74 /* property not found - slightly ambiguous */
78 Class<?> clazz = this.getClass(name);
79 if (this.isListType(name) && result == null) {
81 this.set(convertedName, clazz.newInstance());
82 result = this.get(convertedName);
83 } catch (DynamicException | InstantiationException | IllegalAccessException e) {
91 public Introspector getWrappedValue(String name) {
92 String convertedName = convertPropertyName(name);
95 if (this.hasProperty(name)) {
96 value = this.get(convertedName);
98 /* property not found - slightly ambiguous */
102 Class<?> clazz = this.getClass(name);
103 if (this.isListType(name) && value == null) {
105 this.set(convertedName, clazz.newInstance());
106 value = this.get(convertedName);
107 } catch (DynamicException | InstantiationException | IllegalAccessException e) {
112 return IntrospectorFactory.newInstance(this.getModelType(), value);
120 public List<Introspector> getWrappedListValue(String name) {
121 String convertedName = convertPropertyName(name);
123 List<Introspector> resultList = new ArrayList<>();
124 if (this.hasProperty(name)) {
125 value = this.get(convertedName);
127 /* property not found - slightly ambiguous */
130 boolean isListType = this.isListType(name);
131 if (!this.isListType(name)) {
134 Class<?> clazz = this.getClass(name);
135 if (isListType && value == null) {
137 this.set(convertedName, clazz.newInstance());
138 value = this.get(convertedName);
139 } catch (DynamicException | InstantiationException | IllegalAccessException e) {
144 List<Object> valueList = (List<Object>)value;
146 for (Object item : valueList) {
147 resultList.add(IntrospectorFactory.newInstance(this.getModelType(), item));
154 public Object castValueAccordingToSchema(String name, Object obj) {
156 Class<?> nameClass = this.getClass(name);
157 if (nameClass == null) {
158 throw new IllegalArgumentException("property: " + name + " does not exist on " + this.getDbName());
163 if (!obj.getClass().getName().equals(nameClass.getName())) {
164 if (nameClass.isPrimitive()) {
165 nameClass = ClassUtils.primitiveToWrapper(nameClass);
166 result = nameClass.getConstructor(String.class).newInstance(obj.toString());
168 if (obj instanceof String) {
169 result = nameClass.getConstructor(String.class).newInstance(obj);
170 } else if (!this.isListType(name) && !this.isComplexType(name)){
171 //box = obj.toString();
172 result = nameClass.getConstructor(String.class).newInstance(obj.toString());
175 } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
176 | InvocationTargetException | NoSuchMethodException | SecurityException e) {
177 ErrorLogHelper.logError("AAI_4017", e.getMessage());
183 public List<Object> castValueAccordingToSchema(String name, List<?> objs) {
184 List<Object> result = new ArrayList<>();
186 for (Object item : objs) {
187 result.add(this.castValueAccordingToSchema(name, item));
195 * @param name the property name you'd like to set the value of
196 * @param obj the value to be set
199 public void setValue(String name, Object obj) throws IllegalArgumentException {
200 Object box = this.castValueAccordingToSchema(name, obj);
202 name = convertPropertyName(name);
207 * @return a list of all the properties available on the object
209 public abstract Set<String> getProperties();
211 public Set<String> getProperties(PropertyPredicate<Introspector, String> p) {
212 final Set<String> temp = new LinkedHashSet<>();
213 this.getProperties().stream().filter(item -> {
214 return p.test(this, item);
218 final Set<String> result = Collections.unmodifiableSet(temp);
225 * @return a list of the required properties on the object
227 public abstract Set<String> getRequiredProperties();
230 * @return a list of the properties that can be used to query the object in the db
232 public abstract Set<String> getKeys();
235 * @return a list of the all key properties for this object
237 public Set<String> getAllKeys() {
238 Set<String> result = null;
239 if (this.allKeys == null) {
240 Set<String> keys = this.getKeys();
241 result = new LinkedHashSet<>();
243 String altKeys = this.getMetadata(ObjectMetadata.ALTERNATE_KEYS_1);
244 if (altKeys != null) {
245 String[] altKeysArray = altKeys.split(",");
246 for (String altKey : altKeysArray) {
250 result = Collections.unmodifiableSet(result);
251 this.allKeys = result;
253 result = this.allKeys;
257 public Set<String> getIndexedProperties() {
258 Set<String> result = null;
260 if (this.indexedProperties == null) {
261 result = new LinkedHashSet<>();
262 Set<String> keys = this.getKeys();
264 String altKeys = this.getMetadata(ObjectMetadata.INDEXED_PROPS);
265 if (altKeys != null) {
266 String[] altKeysArray = altKeys.split(",");
267 for (String altKey : altKeysArray) {
271 this.indexedProperties = Collections.unmodifiableSet(result);
273 result = this.indexedProperties;
277 public Set<String> getUniqueProperties() {
278 Set<String> result = null;
279 if (this.uniqueProperties == null) {
280 String altKeys = this.getMetadata(ObjectMetadata.UNIQUE_PROPS);
281 result = new LinkedHashSet<>();
282 if (altKeys != null) {
283 String[] altKeysArray = altKeys.split(",");
284 for (String altKey : altKeysArray) {
288 this.uniqueProperties = Collections.unmodifiableSet(result);
291 result = this.uniqueProperties;
295 public Set<String> getDependentOn() {
296 String dependentOn = this.getMetadata(ObjectMetadata.DEPENDENT_ON);
297 if (dependentOn == null) {
298 return new LinkedHashSet<>();
300 return new LinkedHashSet<>(Arrays.asList(dependentOn.split(",")));
305 * @return the string name of the java class of the named property
307 public String getType(String name) {
308 Class<?> resultClass = this.getClass(name);
311 if (resultClass != null) {
312 result = resultClass.getName();
313 if (result.equals("java.util.ArrayList")) {
314 result = "java.util.List";
321 * This will returned the generic parameterized type of the underlying
322 * object if it exists
324 * @return the generic type of the java class of the underlying object
326 public String getGenericType(String name) {
327 Class<?> resultClass = this.getGenericTypeClass(name);
330 if (resultClass != null) {
331 result = resultClass.getName();
338 * @return the string name of the java class of the underlying object
340 public abstract String getJavaClassName();
344 * @param name the property name
345 * @return the Class object
347 public abstract Class<?> getClass(String name);
349 public abstract Class<?> getGenericTypeClass(String name);
353 * @param name the property name
354 * @return a new instance of the underlying type of this property
355 * @throws AAIUnknownObjectException
357 public Object newInstanceOfProperty(String name) throws AAIUnknownObjectException {
358 String type = this.getType(name);
359 return loader.objectFromName(type);
362 public Object newInstanceOfNestedProperty(String name) throws AAIUnknownObjectException {
363 String type = this.getGenericType(name);
364 return loader.objectFromName(type);
368 public Introspector newIntrospectorInstanceOfProperty(String name) throws AAIUnknownObjectException {
370 Introspector result = IntrospectorFactory.newInstance(this.getModelType(), this.newInstanceOfProperty(name));
376 public Introspector newIntrospectorInstanceOfNestedProperty(String name) throws AAIUnknownObjectException {
378 Introspector result = IntrospectorFactory.newInstance(this.getModelType(), this.newInstanceOfNestedProperty(name));
384 * Is this type not a Java String or primitive
388 public boolean isComplexType(String name) {
389 String result = this.getType(name);
391 if (result.contains("aai") || result.equals("java.lang.Object")) {
398 public boolean isComplexGenericType(String name) {
399 String result = this.getGenericType(name);
401 if (result.contains("aai")) {
408 public boolean isSimpleType(String name) {
409 return !(this.isComplexType(name) || this.isListType(name));
412 public boolean isSimpleGenericType(String name) {
413 return !this.isComplexGenericType(name);
416 public boolean isListType(String name) {
417 String result = this.getType(name);
419 if (result.contains("java.util.List")) {
426 public boolean isContainer() {
427 Set<String> props = this.getProperties();
428 boolean result = false;
429 if (props.size() == 1 && this.isListType(props.iterator().next())) {
436 public abstract String getChildName();
437 public String getChildDBName() {
438 String result = this.getChildName();
440 result = namingException.getDBName(result);
443 public abstract String getName();
445 public String getDbName() {
446 String lowerHyphen = this.getName();
448 lowerHyphen = namingException.getDBName(lowerHyphen);
453 public abstract ModelType getModelType();
455 public boolean hasChild(Introspector child) {
456 boolean result = false;
457 //check all inheriting types for this child
458 if ("true".equals(this.getMetadata(ObjectMetadata.ABSTRACT))) {
459 String[] inheritors = this.getMetadata(ObjectMetadata.INHERITORS).split(",");
460 for (String inheritor : inheritors) {
462 Introspector temp = this.loader.introspectorFromName(inheritor);
463 result = temp.hasProperty(child.getName());
467 } catch (AAIUnknownObjectException e) {
468 LOGGER.warn("Skipping inheritor " + inheritor + " (Unknown Object)", e);
472 result = this.hasProperty(child.getName());
477 public void setURIChain(String uri) {
480 public abstract String getObjectId() throws UnsupportedEncodingException;
482 public String getURI() throws UnsupportedEncodingException {
483 //String result = this.uriChain;
485 String namespace = this.getMetadata(ObjectMetadata.NAMESPACE);
486 String container = this.getMetadata(ObjectMetadata.CONTAINER);
487 if (this.isContainer()) {
488 result += "/" + this.getName();
491 if (container != null) {
492 result += "/" + container;
494 result += "/" + this.getDbName() + "/" + this.findKey();
496 if (namespace != null && !namespace.equals("")) {
497 result = "/" + namespace + result;
505 public String getGenericURI() {
507 if (this.isContainer()) {
508 result += "/" + this.getName();
510 result += "/" + this.getDbName();
511 for (String key : this.getKeys()) {
512 result += "/{" + this.getDbName() + "-" + key + "}";
519 public String getFullGenericURI() {
521 String namespace = this.getMetadata(ObjectMetadata.NAMESPACE);
522 String container = this.getMetadata(ObjectMetadata.CONTAINER);
523 if (this.isContainer()) {
524 result += "/" + this.getName();
528 if (container != null) {
529 result += "/" + container;
531 result += "/" + this.getDbName();
533 for (String key : this.getKeys()) {
534 result += "/{" + this.getDbName() + "-" + key + "}";
536 if (namespace != null && !namespace.equals("")) {
537 result = "/" + namespace + result;
545 public abstract String preProcessKey(String key);
547 protected abstract String findKey() throws UnsupportedEncodingException;
549 public abstract String marshal(MarshallerProperties properties);
551 public abstract Object clone();
553 public abstract Object getUnderlyingObject();
555 public String marshal(boolean formatted) {
556 MarshallerProperties properties =
557 new MarshallerProperties.Builder(MediaType.APPLICATION_JSON_TYPE).formatted(formatted).build();
559 return marshal(properties);
561 public String makeSingular(String word) {
563 String result = word;
564 result = result.replaceAll("(?:([ho])es|s)$", "");
566 if (result.equals("ClassesOfService")) {
567 result = "ClassOfService";
568 } else if (result.equals("CvlanTag")) {
569 result = "CvlanTagEntry";
570 } else if (result.equals("Metadata")) {
571 result = "Metadatum";
576 protected String makePlural(String word) {
577 String result = word;
579 if (result.equals("cvlan-tag-entry")) {
581 } else if (result.equals("class-of-service")) {
582 return "classes-of-service";
583 } else if (result.equals("metadatum")) {
586 result = result.replaceAll("([a-z])$", "$1s");
587 result = result.replaceAll("([hox])s$", "$1es");
589 if (result.equals("classes-of-services")) {
590 result = "classes-of-service";
596 public abstract String getMetadata(ObjectMetadata metadataName);
597 public abstract Map<PropertyMetadata, String> getPropertyMetadata(String propName);
598 public Optional<String> getPropertyMetadata(String propName, PropertyMetadata metadataName) {
599 final String resultValue = this.getPropertyMetadata(propName).getOrDefault(metadataName, "");
600 Optional<String> result = Optional.empty();
602 if (!resultValue.isEmpty()) {
603 result = Optional.of(resultValue);
609 public abstract Version getVersion();
610 public Loader getLoader() {
614 public boolean isTopLevel() {
616 return this.getMetadata(ObjectMetadata.NAMESPACE) != null;