in objectModel/TypeScript/Cdm/CdmEntityAttributeDefinition.ts [240:443]
public constructResolvedAttributes(resOpt: resolveOptions, under?: CdmAttributeContext): ResolvedAttributeSetBuilder {
// let bodyCode = () =>
{
// 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.
let rasb: ResolvedAttributeSetBuilder = new ResolvedAttributeSetBuilder();
const underAtt: CdmAttributeContext = under;
let acpEnt: AttributeContextParameters;
if (!resOpt.inCircularReference) {
if (this.entity?.isProjection) {
// A Projection
// if the max depth is exceeded it should not try to execute the projection
if (!resOpt.depthInfo.maxDepthExceeded) {
const projDef: CdmProjection = this.entity.fetchObjectDefinition<CdmProjection>(resOpt);;
const projDirective: ProjectionDirective = new ProjectionDirective(resOpt, this, this.entity);
const projCtx: ProjectionContext = projDef.constructProjectionContext(projDirective, under);
rasb.ras = projDef.extractResolvedAttributes(projCtx, under);
// from the traits of purpose and applied here
rasb.ras.applyTraits(this.fetchResolvedTraits(resOpt));
}
} else {
// Resolution guidance
resOpt.usedResolutionGuidance = true;
const arc: AttributeResolutionContext = this.fetchAttResContext(resOpt);
const relInfo: relationshipInfo = arc.getRelationshipInfo();
if (underAtt) {
// make a context for this attribute that holds the attributes that come up from the entity
acpEnt = {
under: underAtt,
type: cdmAttributeContextType.entity,
name: this.entity.fetchObjectDefinitionName(),
regarding: this.entity,
includeTraits: true
};
}
if (relInfo.isByRef) {
// make the entity context that a real recursion would have give us
if (under) {
under = rasb.ras.createAttributeContext(resOpt, acpEnt);
}
// if selecting from one of many attributes, then make a context for each one
if (under && relInfo.selectsOne) {
// 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
const entPickFrom: CdmEntityDefinition = this.entity.fetchObjectDefinition(resOpt);
const attsPick: CdmCollection<CdmAttributeItem> = entPickFrom.attributes;
if (entPickFrom && attsPick) {
const l: number = attsPick.length;
for (let i: number = 0; i < l; i++) {
if (attsPick.allItems[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
const acpEntAtt: AttributeContextParameters = {
under: under,
type: cdmAttributeContextType.attributeDefinition,
name: attsPick.allItems[i].fetchObjectDefinitionName(),
regarding: attsPick.allItems[i],
includeTraits: true
};
const pickUnder: CdmAttributeContext = rasb.ras.createAttributeContext(resOpt, acpEntAtt);
// and the entity under that attribute
const pickEnt: CdmEntityReference = (attsPick.allItems[i] as CdmEntityAttributeDefinition).entity;
const pickEntType: cdmAttributeContextType = (pickEnt.fetchObjectDefinition<CdmObjectDefinition>(resOpt).objectType === cdmObjectType.projectionDef) ?
cdmAttributeContextType.projection :
cdmAttributeContextType.entity;
const acpEntAttEnt: AttributeContextParameters = {
under: pickUnder,
type: pickEntType,
name: pickEnt.fetchObjectDefinitionName(),
regarding: pickEnt,
includeTraits: true
};
rasb.ras.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.maxDepthExceeded) {
if (!arc.resOpt.directives) {
arc.resOpt.directives = new AttributeResolutionDirectiveSet();
}
arc.resOpt.directives.add('referenceOnly');
}
} else {
const resLink: resolveOptions = resOpt.copy();
resLink.symbolRefSet = resOpt.symbolRefSet;
rasb.mergeAttributes(this.entity.fetchResolvedAttributes(resLink, acpEnt));
// need to pass up maxDepthExceeded if it was hit
if (resLink.depthInfo && resLink.depthInfo.maxDepthExceeded) {
resOpt.depthInfo = resLink.depthInfo.copy();
}
}
// from the traits of purpose and applied here, see if new attributes get generated
rasb.ras.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.symbolRefSet.merge(arc.resOpt.symbolRefSet);
// use the traits for linked entity identifiers to record the actual foreign key links
if (rasb.ras && rasb.ras.set && (relInfo.isByRef)) {
rasb.ras.set.forEach((att: ResolvedAttribute): void => {
if (att.resolvedTraits) {
const reqdTrait: ResolvedTrait = att.resolvedTraits.find(resOpt, 'is.linkedEntity.identifier');
if (!reqdTrait) {
return;
}
if (reqdTrait.parameterValues === undefined || reqdTrait.parameterValues.length === 0) {
Logger.warning(this.ctx, this.TAG, this.constructResolvedAttributes.name, this.atCorpusPath, cdmLogCode.WarnLinkEntIdentArgsNotSupported, att.displayName, this.entity.namedReference);
return;
}
const entReferences: (string)[] = [];
const attReferences: (string)[] = [];
const addEntityReference: (entRef: CdmEntityReference, namespace: string) => void =
(entityRef: CdmEntityReference, namespace: string): void => {
const entDef: CdmObjectDefinition = entityRef.fetchObjectDefinition(resOpt);
if (entDef) {
const otherResTraits: ResolvedTraitSet = entityRef.fetchResolvedTraits(resOpt);
const identifyingTrait: ResolvedTrait = otherResTraits.find(resOpt, 'is.identifiedBy');
if (otherResTraits && identifyingTrait) {
const attRef: CdmObjectReference = identifyingTrait.parameterValues
.fetchParameterValueByName('attribute').value as CdmObjectReference;
const attNamePath: string = attRef.namedReference;
const attName: string = attNamePath.split('/')
.pop();
// path should be absolute and without a namespace
let absoluteEntPath: string =
this.ctx.corpus.storage.createAbsoluteCorpusPath(entDef.atCorpusPath, entDef.inDocument);
entReferences.push(absoluteEntPath);
attReferences.push(attName);
}
}
};
if (relInfo.selectsOne) {
const entPickFrom: CdmEntityDefinition = this.entity.fetchObjectDefinition(resOpt);
const attsPick: CdmCollection<CdmAttributeItem> = entPickFrom ? entPickFrom.attributes : undefined;
if (entPickFrom && attsPick) {
const l: number = attsPick.length;
for (let i: number = 0; i < l; i++) {
if (attsPick.allItems[i].getObjectType() === cdmObjectType.entityAttributeDef) {
const entAtt: CdmEntityAttributeDefinition = attsPick.allItems[i] as CdmEntityAttributeDefinition;
addEntityReference(entAtt.entity, this.inDocument.namespace);
}
}
}
} else {
addEntityReference(this.entity, this.inDocument.namespace);
}
const cEnt: CdmConstantEntityDefinition =
this.ctx.corpus.MakeObject<CdmConstantEntityDefinition>(cdmObjectType.constantEntityDef);
cEnt.setEntityShape(this.ctx.corpus.MakeRef(cdmObjectType.entityRef, 'entityGroupSet', true));
cEnt.setConstantValues(entReferences.map((entityRef: string, idx: number) => [entityRef, attReferences[idx]]));
const param: CdmObjectReference = this.ctx.corpus.MakeRef(cdmObjectType.entityRef, cEnt, false);
reqdTrait.parameterValues.setParameterValue(resOpt, 'entityReferences', param);
}
});
}
// a 'structured' directive wants to keep all entity attributes together in a group
if (arc.resOpt.directives && arc.resOpt.directives.has('structured')) {
const raSub: ResolvedAttribute = new ResolvedAttribute(
arc.traitsToApply.resOpt, rasb.ras, this.name, rasb.ras.attributeContext);
if (relInfo.isArray) {
// put a resolved trait on this att group, yuck,
// hope I never need to do this again and then need to make a function for this
const tr: CdmTraitReference =
this.ctx.corpus.MakeObject<CdmTraitReference>(cdmObjectType.traitRef, 'is.linkedEntity.array', true);
const t: CdmTraitDefinition = tr.fetchObjectDefinition(resOpt);
const rt: ResolvedTrait = new ResolvedTrait(t, undefined, [], []);
raSub.resolvedTraits = raSub.resolvedTraits.merge(rt, true);
}
const depth: number = rasb.ras.depthTraveled;
rasb = new ResolvedAttributeSetBuilder();
rasb.ras.attributeContext = raSub.attCtx; // this got set to null with the new builder
rasb.ownOne(raSub);
rasb.ras.depthTraveled = depth;
}
}
}
// how ever they got here, mark every attribute from this entity attribute as now being 'owned' by this entityAtt
rasb.ras.setAttributeOwnership(this.name);
rasb.ras.depthTraveled += 1;
return rasb;
}
// return p.measure(bodyCode);
}