in objectModel/Java/objectmodel/src/main/java/com/microsoft/commondatamodel/objectmodel/cdm/CdmEntityAttributeDefinition.java [341:560]
public ResolvedAttributeSetBuilder constructResolvedAttributes(
final ResolveOptions resOpt,
CdmAttributeContext under) {
// find and cache the complete set of attributes
// attributes definitions originate from and then get modified by subsequent re-defintions from (in this order):
// the entity used as an attribute, traits applied to that entity,
// the purpose of the attribute, any traits applied to the attribute.
ResolvedAttributeSetBuilder rasb = new ResolvedAttributeSetBuilder();
final CdmAttributeContext underAtt = under;
AttributeContextParameters acpEnt = null;
if (!resOpt.inCircularReference) {
if (this.entity != null && this.entity.getIsProjection()) {
// A Projection
// if the max depth is exceeded it should not try to execute the projection
if (!resOpt.depthInfo.getMaxDepthExceeded()) {
CdmProjection projDef = this.getEntity().fetchObjectDefinition(resOpt);
ProjectionDirective projDirective = new ProjectionDirective(resOpt, this, this.getEntity());
ProjectionContext projCtx = projDef.constructProjectionContext(projDirective, under);
rasb.setResolvedAttributeSet(projDef.extractResolvedAttributes(projCtx, under));
// from the traits of purpose and applied here
rasb.getResolvedAttributeSet().applyTraits(this.fetchResolvedTraits(resOpt));
}
} else {
// Resolution guidance
resOpt.usedResolutionGuidance = true;
AttributeResolutionContext arc = this.fetchAttResContext(resOpt);
final RelationshipInfo relInfo = arc.getRelationshipInfo();
if (underAtt != null) {
// make a context for this attribute that holds the attributes that come up from the entity
acpEnt = new AttributeContextParameters();
acpEnt.setUnder(underAtt);
acpEnt.setType(CdmAttributeContextType.Entity);
acpEnt.setName(this.getEntity().fetchObjectDefinitionName());
acpEnt.setRegarding(this.getEntity());
acpEnt.setIncludeTraits(true);
}
if (relInfo.isByRef()) {
// make the entity context that a real recursion would have give us
if (under != null) {
under = rasb.getResolvedAttributeSet().createAttributeContext(resOpt, acpEnt);
}
// if selecting from one of many attributes, then make a context for each one
if (under != null && relInfo.doSelectsOne()) {
// the right way to do this is to get a resolved entity from the embedded entity and then
// look through the attribute context hierarchy for non-nested entityReferenceAsAttribute nodes
// that seems like a disaster waiting to happen given endless looping, etc.
// for now, just insist that only the top level entity attributes declared in the ref entity will work
final CdmEntityDefinition entPickFrom = ((CdmEntityReference) this.getEntity()).fetchObjectDefinition(resOpt);
CdmCollection<CdmAttributeItem> attsPick = null;
if (entPickFrom != null) {
attsPick = entPickFrom.getAttributes();
}
if (entPickFrom != null && attsPick != null) {
for (int i = 0; i < attsPick.getCount(); i++) {
if (attsPick.getAllItems().get(i).getObjectType() == CdmObjectType.EntityAttributeDef) {
// a table within a table. as expected with a selectsOne attribute
// since this is by ref, we won't get the atts from the table, but we do need the traits that hold the key
// these are the same contexts that would get created if we recursed
// first this attribute
final AttributeContextParameters acpEntAtt = new AttributeContextParameters();
acpEntAtt.setUnder(under);
acpEntAtt.setType(CdmAttributeContextType.AttributeDefinition);
acpEntAtt.setName(attsPick.getAllItems().get(i).fetchObjectDefinitionName());
acpEntAtt.setRegarding(attsPick.getAllItems().get(i));
acpEntAtt.setIncludeTraits(true);
final CdmAttributeContext pickUnder = rasb.getResolvedAttributeSet().createAttributeContext(resOpt, acpEntAtt);
final CdmEntityReference pickEnt = (((CdmEntityAttributeDefinition) attsPick.getAllItems().get(i))).getEntity();
CdmAttributeContextType pickEntType = (pickEnt.fetchObjectDefinition(resOpt).getObjectType() == CdmObjectType.ProjectionDef) ?
CdmAttributeContextType.Projection :
CdmAttributeContextType.Entity;
final AttributeContextParameters acpEntAttEnt = new AttributeContextParameters();
acpEntAttEnt.setUnder(pickUnder);
acpEntAttEnt.setType(pickEntType);
acpEntAttEnt.setName(pickEnt.fetchObjectDefinitionName());
acpEntAttEnt.setRegarding(pickEnt);
acpEntAttEnt.setIncludeTraits(true);
rasb.getResolvedAttributeSet().createAttributeContext(resOpt, acpEntAttEnt);
}
}
}
}
// if we got here because of the max depth, need to impose the directives to make the trait work as expected
if (resOpt.depthInfo.getMaxDepthExceeded()) {
if (arc.getResOpt().getDirectives() == null) {
arc.getResOpt().setDirectives(new AttributeResolutionDirectiveSet());
}
arc.getResOpt().getDirectives().add("referenceOnly");
}
} else {
final ResolveOptions resLink = resOpt.copy();
resLink.setSymbolRefSet(resOpt.getSymbolRefSet());
rasb.mergeAttributes(this.getEntity().fetchResolvedAttributes(resLink, acpEnt));
// need to pass up maxDepthExceeded if it was hit
if (resLink.depthInfo.getMaxDepthExceeded()) {
resOpt.depthInfo = resLink.depthInfo.copy();
}
}
// from the traits of purpose and applied here, see if new attributes get generated
rasb.getResolvedAttributeSet().setAttributeContext(underAtt);
rasb.applyTraits(arc);
rasb.generateApplierAttributes(arc, true); // true = apply the prepared traits to new atts
// this may have added symbols to the dependencies, so merge them
resOpt.getSymbolRefSet().merge(arc.getResOpt().getSymbolRefSet());
// use the traits for linked entity identifiers to record the actual foreign key links
if (rasb.getResolvedAttributeSet() != null && rasb.getResolvedAttributeSet().getSet() != null
&& relInfo.isByRef()) {
for (final ResolvedAttribute att : rasb.getResolvedAttributeSet().getSet()) {
if (att.getResolvedTraits() != null) {
final ResolvedTrait reqdTrait = att.getResolvedTraits()
.find(resOpt, "is.linkedEntity.identifier");
if (reqdTrait == null) {
continue;
}
if (reqdTrait.getParameterValues() == null
|| reqdTrait.getParameterValues().length() == 0) {
Logger.warning(this.getCtx(), TAG, "constructResolvedAttributes", this.getAtCorpusPath(), CdmLogCode.WarnLinkEntIdentArgsNotSupported, att.displayName(), this.getEntity().getNamedReference());
continue;
}
final List<String> entReferences = new ArrayList<>();
final List<String> attReferences = new ArrayList<>();
if (relInfo.doSelectsOne()) {
final CdmEntityDefinition entPickFrom = (((CdmEntityReference) this.getEntity())).fetchObjectDefinition(resOpt);
List<CdmObject> attsPick = null;
if (entPickFrom != null && entPickFrom.getAttributes() != null) {
attsPick = entPickFrom.getAttributes().getAllItems()
.stream()
.map(attribute -> (CdmObject) attribute)
.collect(Collectors.toList());
}
if (entPickFrom != null && attsPick != null) {
for (int i = 0; i < attsPick.size(); i++) {
if (attsPick.get(i).getObjectType() == CdmObjectType.EntityAttributeDef) {
final CdmEntityAttributeDefinition entAtt = (CdmEntityAttributeDefinition) attsPick.get(i);
addEntityReference(entAtt.getEntity(), resOpt, entReferences, attReferences, this.getInDocument().getNamespace());
}
}
}
} else {
addEntityReference(
this.getEntity(),
resOpt,
entReferences,
attReferences,
this.getInDocument() == null
? null
: this.getInDocument().getNamespace());
}
final CdmConstantEntityDefinition constantEntity = this.getCtx().getCorpus()
.makeObject(CdmObjectType.ConstantEntityDef);
constantEntity.setEntityShape(
this.getCtx().getCorpus().makeRef(CdmObjectType.EntityRef, "entityGroupSet", true));
final List<List<String>> listOfStringLists = new ArrayList<>();
for (int i = 0; i < entReferences.size(); i++) {
final List<String> stringList = new ArrayList<>();
stringList.add(entReferences.get(i));
stringList.add(attReferences.get(i));
listOfStringLists.add(stringList);
}
constantEntity.setConstantValues(listOfStringLists);
final CdmEntityReference traitParam = this.getCtx().getCorpus()
.makeRef(CdmObjectType.EntityRef, constantEntity, false);
reqdTrait.getParameterValues().setParameterValue(resOpt, "entityReferences", traitParam);
}
}
}
// a 'structured' directive wants to keep all entity attributes together in a group
if (arc.getResOpt().getDirectives() != null && arc.getResOpt().getDirectives().has("structured")) {
// make one resolved attribute with a name from this entityAttribute that contains the set
// of atts we just put together.
final ResolvedAttribute raSub = new ResolvedAttribute(arc.getTraitsToApply().getResOpt(),
rasb.getResolvedAttributeSet(),
this.getName(),
rasb.getResolvedAttributeSet().getAttributeContext());
if (relInfo.isArray()) {
// put a resolved trait on this att group, hope I never need to do this again and then need to make a function for this
final CdmTraitReference tr = this.getCtx().getCorpus()
.makeObject(CdmObjectType.TraitRef, "is.linkedEntity.array", true);
final CdmTraitDefinition t = tr.fetchObjectDefinition(resOpt);
final ResolvedTrait rt = new ResolvedTrait(t, null, new ArrayList<>(), new ArrayList<>());
raSub.setResolvedTraits(raSub.getResolvedTraits().merge(rt, true));
}
int depth = rasb.getResolvedAttributeSet().getDepthTraveled();
rasb = new ResolvedAttributeSetBuilder();
rasb.getResolvedAttributeSet().setAttributeContext(raSub.getAttCtx()); // this got set to null with the new builder
rasb.ownOne(raSub);
rasb.getResolvedAttributeSet().setDepthTraveled(depth);
}
}
}
// how ever they got here, mark every attribute from this entity attribute as now being 'owned' by this entityAtt
rasb.getResolvedAttributeSet().setAttributeOwnership(this.getName());
rasb.getResolvedAttributeSet().setDepthTraveled(rasb.getResolvedAttributeSet().getDepthTraveled() + 1);
return rasb;
}