/// <summary> /// Helper method to recursively walk value roles. A value role /// is any role that is allowed to have a value constraint. /// </summary> /// <param name="playedRoles">Roles from an ObjectType to walk. The assumption is made that the /// owning ObjectType is either a value type or has a preferred identifier with exactly one role</param> /// <param name="dataTypeLink">The data type information for the constraint</param> /// <param name="pathRoots">The <see cref="RolePathObjectTypeRoot"/> relationships from a role path associated /// with the root path.</param> /// <param name="previousValueConstraint">The value constraint nearest this value role. /// Any value constraint on the current set of roles must be a subset of the previousValueConstraint.</param> /// <param name="skipRole">A role to skip. If the playedRoles came from a preferred identifier, /// then the skipRole is the opposite role.</param> /// <param name="walkSubtypes">true to walk subtypes. Should be true if the playedRoles come from an /// EntityType and false if they come from a ValueType</param> /// <param name="visitor">The callback delegate</param> /// <returns>true to continue iteration</returns> private static bool WalkDescendedValueRoles(IList<Role> playedRoles, IList<RolePathObjectTypeRoot> pathRoots, ValueTypeHasDataType dataTypeLink, ValueConstraint previousValueConstraint, Role skipRole, bool walkSubtypes, ValueRoleVisitor visitor) { int count = pathRoots.Count; for (int i = 0; i < count; ++i) { RolePathObjectTypeRoot pathRoot = pathRoots[i]; if (!visitor(null, null, pathRoot, dataTypeLink, pathRoot.ValueConstraint, previousValueConstraint)) { return false; } } count = playedRoles.Count; for (int i = 0; i < count; ++i) { Role role = playedRoles[i]; SupertypeMetaRole supertypeRole; if (role == skipRole) { // Nothing to do } else if (null != (supertypeRole = role as SupertypeMetaRole)) { if (walkSubtypes) { SubtypeFact subtypeFact = (SubtypeFact)role.FactType; ObjectType subtype; if (subtypeFact.ProvidesPreferredIdentifier && null != (subtype = subtypeFact.Subtype) && subtype.PreferredIdentifier == null) { if (!WalkDescendedValueRoles(subtype.PlayedRoleCollection, RolePathObjectTypeRoot.GetLinksToRolePathCollection(subtype), dataTypeLink, previousValueConstraint, null, true, visitor)) { return false; } } } } else if (!(role is SubtypeMetaRole)) { RoleValueConstraint currentValueConstraint = role.ValueConstraint; if (!visitor(role, null, null, dataTypeLink, currentValueConstraint, previousValueConstraint)) { return false; } if (currentValueConstraint != null && !currentValueConstraint.IsDeleted) { previousValueConstraint = currentValueConstraint; } foreach (PathedRole pathedRole in PathedRole.GetLinksToRolePathCollection(role)) { // UNDONE: VALUEROLE This does not correctly report a value constraint from a previous // path node. Note that this, as well as allowing value restrictions on supertype roles // (and possibly other patterns), can result in multiple previous value constraints, so // the callback signature may possibly need to be modified here. As of changeset 1442, // none of the callbacks use the previousValueConstraint information, so we can ignore // this for now. // Note that we visit for the pathed role even if no value constraint is present // to allow processing for this pathed role. if (!visitor(role, pathedRole, null, dataTypeLink, pathedRole.ValueConstraint, previousValueConstraint)) { return false; } } // Walk sequences to find a single-role preferred identifier so // we can get to the next link. LinkedElementCollection<ConstraintRoleSequence> sequences = role.ConstraintRoleSequenceCollection; int sequencesCount = sequences.Count; for (int j = 0; j < sequencesCount; ++j) { UniquenessConstraint constraint = sequences[j] as UniquenessConstraint; ObjectType identifierFor; if (null != (constraint = sequences[j] as UniquenessConstraint) && null != (identifierFor = constraint.PreferredIdentifierFor) && constraint.RoleCollection.Count == 1) { RoleBase nextSkipRole = role.OppositeRoleAlwaysResolveProxy; if (nextSkipRole == null) { return false; } if (!WalkDescendedValueRoles(identifierFor.PlayedRoleCollection, RolePathObjectTypeRoot.GetLinksToRolePathCollection(identifierFor), dataTypeLink, previousValueConstraint, nextSkipRole.Role, true, visitor)) { return false; } } } } } return true; }
/// <summary> /// Get all value roles including all roles directly attached to the provided /// object type and any roles descended from this one through prefererred identifiers. /// Walks the opposite direction of <see cref="Role.GetValueRoles()"/> /// </summary> /// <param name="anchorType">The <see cref="ObjectType"/> to walk descended roles for</param> /// <param name="unattachedRole">A role to test that is not currently attached to the anchorType. /// If unattachedRole is not null, then only this role will be tested. Otherwise, all current played /// roles will be walked.</param> /// <param name="unattachedPreferredIdentifier">A preferred identifier to test that is not currently /// attached to the anchorType.</param> /// <param name="visitor">A <see cref="ValueRoleVisitor"/> callback delegate.</param> public static void WalkDescendedValueRoles(ObjectType anchorType, Role unattachedRole, UniquenessConstraint unattachedPreferredIdentifier, ValueRoleVisitor visitor) { ValueTypeHasDataType dataTypeLink = anchorType.GetDataTypeLink(); if (null == unattachedPreferredIdentifier && null != (dataTypeLink = anchorType.GetDataTypeLink())) { ObjectType unattachedRolePlayer; WalkDescendedValueRoles( (unattachedRole != null) ? new Role[] { unattachedRole } as IList<Role> : anchorType.PlayedRoleCollection, RolePathObjectTypeRoot.GetLinksToRolePathCollection(anchorType), dataTypeLink, anchorType.ValueConstraint, null, (null == unattachedRole || null == (unattachedRolePlayer = unattachedRole.RolePlayer)) ? false : !unattachedRolePlayer.IsValueType, visitor); } else { LinkedElementCollection<Role> roles; UniquenessConstraint preferredIdentifier; if (null != (preferredIdentifier = unattachedPreferredIdentifier ?? anchorType.ResolvedPreferredIdentifier) && (roles = preferredIdentifier.RoleCollection).Count == 1) { Role currentRole = roles[0]; Role[] valueRoles = currentRole.GetValueRoles(); if (valueRoles != null) { ValueConstraint nearestValueConstraint = null; int valueRolesCount = valueRoles.Length; for (int i = valueRolesCount - 1; i >= 0; --i) { nearestValueConstraint = valueRoles[i].ValueConstraint; if (nearestValueConstraint != null) { break; } } ObjectType valueType = valueRoles[0].RolePlayer; dataTypeLink = valueType.GetDataTypeLink(); if (nearestValueConstraint == null) { nearestValueConstraint = valueType.ValueConstraint; } RoleBase nextSkipRole = currentRole.OppositeRoleAlwaysResolveProxy; if (nextSkipRole != null) { WalkDescendedValueRoles( (unattachedRole != null) ? new Role[] { unattachedRole } as IList<Role> : anchorType.PlayedRoleCollection, RolePathObjectTypeRoot.GetLinksToRolePathCollection(anchorType), dataTypeLink, nearestValueConstraint, nextSkipRole.Role, true, visitor); } } } } }