public ResolvedAttributeSetBuilder constructResolvedAttributes()

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;
  }