/**
* ============LICENSE_START=======================================================
* org.onap.aai
* ================================================================================
* Copyright © 2017 AT&T Intellectual Property. All rights reserved.
* Copyright © 2017 Amdocs
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============LICENSE_END=========================================================
*
* ECOMP is a trademark and service mark of AT&T Intellectual Property.
*/
package org.openecomp.datarouter.util;
import org.eclipse.persistence.dynamic.DynamicType;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.oxm.XPathFragment;
import org.eclipse.persistence.internal.oxm.mappings.Descriptor;
import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.oxm.XMLField;
import org.onap.aai.datarouter.entity.OxmEntityDescriptor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
/**
* Builds up a representation of the versioned entities in a way that they can be cross referenced
* in a data-driven way
*
* @author DAVEA
*/
public class VersionedOxmEntities {
private static final String REST_ROOT_ENTITY = "inventory";
private HashMap crossEntityReferenceContainerLookup = new HashMap<>();
private HashMap crossEntityReferenceLookup = new HashMap<>();
private Map entityTypeLookup = new LinkedHashMap<>();
private Map searchableEntityDescriptors = new HashMap<>();
private Map suggestableEntityDescriptors = new HashMap<>();
private Map entityAliasDescriptors = new HashMap<>();
public void initialize(DynamicJAXBContext context) {
parseOxmContext(context);
buildCrossEntityReferenceCollections(REST_ROOT_ENTITY, new HashSet());
populateSearchableDescriptors(context);
}
/**
* The big goal for these methods is to make the processing as generic and model driven as possible.
* There are only two exceptions to this rule, at the moment. I needed to hard-coded the top level REST data
* model entity type, which is "inventory" for now. And as this class is heavily focused and coupled towards
* building a version specific set of lookup structures for the "crossEntityReference" model attribute, it possesses
* knowledge of that attribute whether it exists or not in the DynamicJAXBContext we are currently analyzing.
*
* This method will build two collections:
*
* 1) A list of entity types that can have nested entities containing cross entity reference definitions.
* The purpose of this collection is a fail-fast test when processing UEB events so we can quickly determine if
* it is necessary to deeply parse the event looking for cross entity reference attributes which not exist.
*
* For example, looking at a service-instance <=> inventory path:
*
* inventory (true)
* -> business (true)
* -> customers (true)
* -> customer (true)
* -> service-subscriptions (true)
* -> service-subscription (CER defined here in the model) (true)
* -> service-instances (false)
* -> service-instance (false)
*
* Because service-subscription contains a model definition of CER, in the first collection all the types in the
* tree will indicate that it possesses one or more contained entity types with a cross-entity-reference definition.
*
* 2) A lookup for { entityType => CrossEntityReference } so we can quickly access the model definition of a CER
* for a specific entity type when we begin extracting parent attributes for transposition into nested child entity
* types.
*
* @param entityType
* @param checked
* @return
*/
protected boolean buildCrossEntityReferenceCollections(String entityType, HashSet checked) {
/*
* To short-circuit infinite loops, make sure this entityType hasn't
* already been checked
*/
if (checked.contains(entityType)) {
return false;
} else {
checked.add(entityType);
}
DynamicType parentType = entityTypeLookup.get(entityType);
DynamicType childType;
boolean returnValue = false;
if (parentType == null) {
return returnValue;
}
/*
* Check if current descriptor contains the cross-entity-reference
* attribute. If it does not walk the entity model looking for nested
* entity types that may contain the reference.
*/
Map properties = parentType.getDescriptor().getProperties();
if (properties != null) {
for (Map.Entry entry : properties.entrySet()) {
if ("crossEntityReference".equalsIgnoreCase(entry.getKey())) {
returnValue = true;
CrossEntityReference cer = new CrossEntityReference();
cer.initialize(entry.getValue());
crossEntityReferenceLookup.put(entityType, cer);
//System.out.println("entityType = " + entityType + " contains a CER instance = " + returnValue);
// return true;
}
}
}
Vector fields = parentType.getDescriptor().getAllFields();
if (fields != null) {
XMLField xmlField;
for (DatabaseField f : fields) {
if (f instanceof XMLField) {
xmlField = (XMLField) f;
XPathFragment xpathFragment = xmlField.getXPathFragment();
String entityShortName = xpathFragment.getLocalName();
childType = entityTypeLookup.get(entityShortName);
if (childType != null) {
if (!checked.contains(entityShortName)) {
if (buildCrossEntityReferenceCollections(entityShortName, checked)) {
returnValue = true;
}
}
checked.add(entityShortName);
}
}
}
}
crossEntityReferenceContainerLookup.put(entityType, Boolean.valueOf(returnValue));
return returnValue;
}
private void populateSearchableDescriptors(DynamicJAXBContext oxmContext) {
List descriptorsList = oxmContext.getXMLContext().getDescriptors();
OxmEntityDescriptor newOxmEntity;
for (Descriptor desc : descriptorsList) {
DynamicType entity = (DynamicType) oxmContext.getDynamicType(desc.getAlias());
//LinkedHashMap oxmProperties = new LinkedHashMap();
String primaryKeyAttributeNames = null;
//Not all fields have key attributes
if (desc.getPrimaryKeyFields() != null) {
primaryKeyAttributeNames = desc.getPrimaryKeyFields()
.toString().replaceAll("/text\\(\\)", "").replaceAll("\\[", "").replaceAll("\\]", "");
}
String entityName = desc.getDefaultRootElement();
Map properties = entity.getDescriptor().getProperties();
if (properties != null) {
for (Map.Entry entry : properties.entrySet()) {
if ("searchable".equalsIgnoreCase(entry.getKey())) {
/*
* we can do all the work here, we don't have a create additional collections for
* subsequent passes
*/
newOxmEntity = new OxmEntityDescriptor();
newOxmEntity.setEntityName(entityName);
newOxmEntity
.setPrimaryKeyAttributeName(Arrays.asList(primaryKeyAttributeNames.split(",")));
newOxmEntity.setSearchableAttributes(Arrays.asList(entry.getValue().split(",")));
searchableEntityDescriptors.put(entityName, newOxmEntity);
} else if ("containsSuggestibleProps".equalsIgnoreCase(entry.getKey())) {
newOxmEntity = new OxmEntityDescriptor();
newOxmEntity.setEntityName(entityName);
newOxmEntity.setSuggestableEntity(true);
Vector descriptorMaps = entity.getDescriptor().getMappings();
List listOfSuggestableAttributes = new ArrayList<>();
for (DatabaseMapping descMap : descriptorMaps) {
if (descMap.isAbstractDirectMapping()) {
if (descMap.getProperties().get("suggestibleOnSearch") != null) {
String suggestableOnSearchString = String.valueOf(
descMap.getProperties().get("suggestibleOnSearch"));
boolean isSuggestibleOnSearch = Boolean.valueOf(suggestableOnSearchString);
if (isSuggestibleOnSearch) {
/* Grab attribute types for suggestion */
String attributeName = descMap.getField().getName()
.replaceAll("/text\\(\\)", "");
listOfSuggestableAttributes.add(attributeName);
}
}
}
}
newOxmEntity.setSuggestableAttributes(listOfSuggestableAttributes);
suggestableEntityDescriptors.put(entityName, newOxmEntity);
} else if ("suggestionAliases".equalsIgnoreCase(entry.getKey())) {
newOxmEntity = new OxmEntityDescriptor();
newOxmEntity.setEntityName(entityName);
newOxmEntity.setAlias(Arrays.asList(entry.getValue().split(",")));
entityAliasDescriptors.put(entityName, newOxmEntity);
}
}
}
}
}
public Map getSearchableEntityDescriptors() {
return searchableEntityDescriptors;
}
public OxmEntityDescriptor getSearchableEntityDescriptor(String entityType) {
return searchableEntityDescriptors.get(entityType);
}
public HashMap getCrossEntityReferenceContainers() {
return crossEntityReferenceContainerLookup;
}
public HashMap getCrossEntityReferences() {
return crossEntityReferenceLookup;
}
private void parseOxmContext(DynamicJAXBContext oxmContext) {
List descriptorsList = oxmContext.getXMLContext().getDescriptors();
for (Descriptor desc : descriptorsList) {
DynamicType entity = (DynamicType) oxmContext.getDynamicType(desc.getAlias());
String entityName = desc.getDefaultRootElement();
entityTypeLookup.put(entityName, entity);
}
}
public boolean entityModelContainsCrossEntityReference(String containerEntityType) {
Boolean v = crossEntityReferenceContainerLookup.get(containerEntityType);
if (v == null) {
return false;
}
return v;
}
public boolean entityContainsCrossEntityReference(String entityType) {
return crossEntityReferenceLookup.get(entityType) != null;
}
public CrossEntityReference getCrossEntityReference(String entityType) {
return crossEntityReferenceLookup.get(entityType);
}
public Map getSuggestableEntityDescriptors() {
return suggestableEntityDescriptors;
}
public void setSuggestableEntityDescriptors(
Map suggestableEntityDescriptors) {
this.suggestableEntityDescriptors = suggestableEntityDescriptors;
}
public Map getEntityAliasDescriptors() {
return entityAliasDescriptors;
}
public void setEntityAliasDescriptors(Map entityAliasDescriptors) {
this.entityAliasDescriptors = entityAliasDescriptors;
}
public void extractEntities(String entityType, DynamicJAXBContext context, Collection entities) {
}
public String dumpCrossEntityReferenceContainers() {
Set keys = crossEntityReferenceContainerLookup.keySet();
StringBuilder sb = new StringBuilder(128);
for (String key : keys) {
if (crossEntityReferenceContainerLookup.get(key)) {
sb.append("\n").append("Entity-Type = '" + key + "' contains a Cross-Entity-Reference.");
}
}
return sb.toString();
}
}