/// <summary> /// Verify the <see cref="RequirementsGroup"/> against this <see cref="ParameterizedCategoryRule"/> /// </summary> /// <param name="requirementsContainer">The <see cref="RequirementsContainer"/> container to check</param> /// <param name="violations">The collection of <see cref="RuleViolation"/> to update</param> private void VerifyGroup(RequirementsContainer requirementsContainer, List <RuleViolation> violations) { foreach (var group in requirementsContainer.Group) { if (group.IsMemberOfCategory(this.Category)) { var missingParameterTypes = new List <ParameterType>(); foreach (var parameterType in this.ParameterType) { var parameter = group.ParameterValue.SingleOrDefault(p => p.ParameterType == parameterType); if (parameter == null) { missingParameterTypes.Add(parameterType); } } if (missingParameterTypes.Count > 0) { var iids = string.Join(",", missingParameterTypes.Select(x => x.Iid)); var shortnames = string.Join(",", missingParameterTypes.Select(x => x.ShortName)); var violation = new RuleViolation(Guid.NewGuid(), this.Cache, this.IDalUri); violation.RuleViolatedClassKind.Add(group.ClassKind); violation.ViolatingThing.Add(group.Iid); violation.Description = $"The RequirementsGroup {@group.Name} does not contain parameters that reference the following parameter types {iids} with shortnames: {shortnames}"; violations.Add(violation); } } this.VerifyGroup(group, violations); } }
/// <summary> /// Query the <see cref="RequirementsContainerParameterValue"/> if it exists or create a new one /// </summary> /// <param name="reqContainer">The <see cref="RequirementsContainer"/> container</param> /// <param name="simpleParameterValue">The queried <see cref="RequirementsContainerParameterValue"/></param> /// <returns>A value indicating if the <see cref="RequirementsContainerParameterValue"/> existed</returns> protected bool TryQueryOrderParameterOrCreate(RequirementsContainer reqContainer, out RequirementsContainerParameterValue simpleParameterValue) { var parameterValue = this.QueryOrderParameter(reqContainer); if (parameterValue != null) { simpleParameterValue = parameterValue; return(true); } var iteration = reqContainer.GetContainerOfType <Iteration>(); Tuple <DomainOfExpertise, Participant> domainTuple; if (!this.session.OpenIterations.TryGetValue(iteration, out domainTuple) || domainTuple.Item1 == null) { throw new InvalidOperationException("The domain is null."); } simpleParameterValue = new RequirementsContainerParameterValue(Guid.NewGuid(), null, null) { ParameterType = this.reqOrderParameterType, Value = new ValueArray <string>(new[] { int.MaxValue.ToString() }), }; return(false); }
/// <summary> /// Populate the <paramref name="thingsToCheck"/> /// </summary> /// <param name="reqContainer">The <see cref="RequirementsContainer"/></param> /// <param name="thingsToCheck">The collection of <see cref="Guid"/> to populate</param> private void AddThingsToVerify(RequirementsContainer reqContainer, List <Guid> thingsToCheck) { foreach (var group in reqContainer.Group) { thingsToCheck.Add(group.Iid); this.AddThingsToVerify(group, thingsToCheck); } }
/// <summary> /// Checks the validity of the <see cref="RequirementsContainer"/> ShortName property /// </summary> /// <param name="requirementsContainer"> /// the subject <see cref="RequirementsContainer"/> /// </param> /// <param name="rule"> /// the corresponding <see cref="IRule"/> /// </param> /// <returns> /// A instance of <see cref="RuleCheckResult"/> when the rule is violated, or null when all is good /// </returns> private RuleCheckResult CheckShortNameValidityOfRequirementsContainer(RequirementsContainer requirementsContainer, IRule rule) { if (!Regex.IsMatch(requirementsContainer.ShortName, "^[a-zA-Z][a-zA-Z0-9_]*$")) { return(new RuleCheckResult(requirementsContainer, rule.Id, $"The ShortName: {requirementsContainer.ShortName} is invalid. The ShortName must start with a letter and not contain any spaces or non alphanumeric characters.", rule.Severity)); } return(null); }
/// <summary> /// Query the <see cref="SimpleParameterValue"/> representing the order /// </summary> /// <param name="reqContainer">The <see cref="RequirementsContainer"/> container</param> /// <returns>The <see cref="SimpleParameterValue"/></returns> protected RequirementsContainerParameterValue QueryOrderParameter(RequirementsContainer reqContainer) { if (RequirementsModule.PluginSettings?.OrderSettings == null) { throw new InvalidOperationException("No setting for the order-parameter"); } return(reqContainer.ParameterValue.FirstOrDefault(x => x.ParameterType.Iid == RequirementsModule.PluginSettings.OrderSettings.ParameterType)); }
/// <summary> /// Computes the bread crumb of a <see cref="RequirementsContainer"/> /// </summary> /// <param name="requirementsContainer"> /// The <see cref="RequirementsContainer"/> of which the bread crumb is to be computed /// </param> /// <returns> /// RequirementsSpecification: S:ShortName /// RequirementsGroup: RG:ShortName /// </returns> public static string BreadCrumb(this RequirementsContainer requirementsContainer) { var container = requirementsContainer.Container as RequirementsContainer; if (container != null) { return(string.Format("{0}.{1}", container.BreadCrumb(), requirementsContainer.BreadCrumbPart())); } return(BreadCrumbPart(requirementsContainer)); }
/// <summary> /// Computes the Bread Crumb Part of a <see cref="RequirementsContainer"/> /// </summary> /// <param name="requirementsContainer"> /// The <see cref="RequirementsContainer"/> of which the part is computed /// </param> /// <returns> /// RequirementsSpecification: S:ShortName /// RequirementsGroup: RG:ShortName /// </returns> public static string BreadCrumbPart(this RequirementsContainer requirementsContainer) { var requirementsSpecification = requirementsContainer as RequirementsSpecification; if (requirementsSpecification != null) { return(string.Format("S:{0}", requirementsSpecification.ShortName)); } var requirementsGroup = requirementsContainer as RequirementsGroup; if (requirementsGroup != null) { return(string.Format("RG:{0}", requirementsGroup.ShortName)); } throw new InvalidOperationException(string.Format("{0} is not supported by the BreadCrumbComputer", requirementsContainer.GetType())); }
/// <summary> /// Add a <see cref="RequirementsContainerParameterValue"/> to a <see cref="RequirementsContainer"/> /// </summary> /// <param name="requirementContainer">The <see cref="RequirementsContainer"/></param> /// <param name="value">The <see cref="AttributeValue"/></param> private void SetParameterValue(RequirementsContainer requirementContainer, AttributeValue value) { if (this.datatypeDefMap[value.AttributeDefinition.DatatypeDefinition].ParameterType == null) { throw new InvalidOperationException("The datatype-definition of an AttributeValue that is mapped to a Parameter-Value shall be mapped to a ParameterType."); } var theValue = this.GetAttributeValue(value); var valueArray = new string[] { theValue }; // TODO get mapped scale var simpleParameter = new RequirementsContainerParameterValue { ParameterType = this.datatypeDefMap[value.AttributeDefinition.DatatypeDefinition].ParameterType }; simpleParameter.Value = new ValueArray <string>(valueArray); requirementContainer.ParameterValue.Add(simpleParameter); }
/// <summary> /// Change the container of the <paramref name="reqContainerToInsert"/> to insert /// </summary> /// <param name="transaction">The current transaction</param> /// <param name="reqContainerToInsert">The <see cref="RequirementsContainer"/> to insert</param> /// <param name="referenceGroup">The <see cref="RequirementsContainer"/> to insert before or after</param> protected override void ChangeContainer(ThingTransaction transaction, RequirementsContainer reqContainerToInsert, RequirementsContainer referenceGroup) { // create a clone if there is none var reqToInsertClone = (RequirementsGroup)transaction.UpdatedThing.SingleOrDefault(x => x.Key.Iid == reqContainerToInsert.Iid).Value; if (reqToInsertClone == null) { reqToInsertClone = (RequirementsGroup)reqContainerToInsert.Clone(false); transaction.CreateOrUpdate(reqToInsertClone); } // change container if necessary if (reqContainerToInsert.Container != referenceGroup.Container) { var reqContainerClone = (RequirementsContainer)referenceGroup.Container.Clone(false); reqContainerClone.Group.Add(reqToInsertClone); transaction.CreateOrUpdate(reqContainerClone); } }
/// <summary> /// Create 10-25 <see cref="Thing"/>s from the <see cref="SpecHierarchy"/> /// </summary> /// <param name="specHierarchy">The <see cref="SpecHierarchy"/></param> /// <param name="reqContainer">The <see cref="RequirementsContainer"/> representing the current level of requirement</param> private void ComputeRequirementFromSpecHierarchy(SpecHierarchy specHierarchy, RequirementsContainer reqContainer) { // create a group if the specHierarchy has children if (specHierarchy.Children.Any()) { var group = this.CreateRequirementGroup(specHierarchy.Object); reqContainer.Group.Add(group); foreach (var hierarchy in specHierarchy.Children) { this.ComputeRequirementFromSpecHierarchy(hierarchy, group); } } SpecTypeMap specTypeMapping; if (!this.typeMap.TryGetValue(specHierarchy.Object.Type, out specTypeMapping)) { // The instance of this type shall not be generated return; } var specObjectTypeMap = (SpecObjectTypeMap)specTypeMapping; if (!specObjectTypeMap.IsRequirement) { var group = this.CreateRequirementGroup(specHierarchy.Object); reqContainer.Group.Add(group); return; } var requirement = this.CreateRequirement(specHierarchy.Object); if (requirement != null) { var group = reqContainer as RequirementsGroup; if (group != null) { requirement.Group = group; } RequirementsSpecification container = null; var specification = reqContainer as RequirementsSpecification; container = specification ?? reqContainer.GetContainerOfType <RequirementsSpecification>(); if (container == null) { throw new InvalidOperationException("The RequirementsSpecication container is null."); } container.Requirement.Add(requirement); } }
/// <summary> /// Change the container of the <paramref name="reqContainerToInsert"/> to insert /// </summary> /// <param name="transaction">The current transaction</param> /// <param name="reqContainerToInsert">The <see cref="RequirementsContainer"/> to insert</param> /// <param name="referenceGroup">The <see cref="RequirementsContainer"/> to insert before or after</param> protected override void ChangeContainer(ThingTransaction transaction, RequirementsContainer reqContainerToInsert, RequirementsContainer referenceGroup) { // do nothing - Specification cannot have their container changed }
/// <summary> /// Change the <see cref="IReadOnlyList{T}"/> within the current context /// </summary> /// <param name="reqContainerToInsert">The <see cref="RequirementsContainer"/> to insert</param> /// <param name="referenceGroup">The <see cref="RequirementsContainer"/> to insert before or after</param> protected override IReadOnlyList <RequirementsContainer> QueryAllContextReqContainer(RequirementsContainer reqContainerToInsert, RequirementsContainer referenceGroup) { var iteration = (Iteration)referenceGroup.Container; return(iteration.RequirementsSpecification); }
/// <summary> /// Change the <see cref="IReadOnlyList{T}"/> within the current context /// </summary> /// <param name="reqContainerToInsert">The <see cref="RequirementsContainer"/> to insert</param> /// <param name="referenceGroup">The <see cref="RequirementsContainer"/> to insert before or after</param> protected override IReadOnlyList <RequirementsContainer> QueryAllContextReqContainer(RequirementsContainer reqContainerToInsert, RequirementsContainer referenceGroup) { var reqContainer = (RequirementsContainer)referenceGroup.Container; var allGroupInContext = reqContainer.Group.Where(x => x.Iid != reqContainerToInsert.Iid).ToList(); allGroupInContext.Add((RequirementsGroup)reqContainerToInsert); return(allGroupInContext); }
/// <summary> /// Creates and returns a <see cref="ThingTransaction"/> that contains required operations to insert <paramref name="groupToInsert"/> before <paramref name="referenceGroup"/> /// </summary> /// <param name="groupToInsert">The <see cref="T"/> to insert</param> /// <param name="referenceGroup">The <see cref="T"/> that comes right after <paramref name="groupToInsert"/></param> /// <param name="insertKind">The <see cref="InsertKind"/></param> /// <returns>The <see cref="ThingTransaction"/></returns> public ThingTransaction Insert(RequirementsContainer groupToInsert, RequirementsContainer referenceGroup, InsertKind insertKind) { var transaction = new ThingTransaction(TransactionContextResolver.ResolveContext(groupToInsert)); var allGroupInContext = this.QueryAllContextReqContainer(groupToInsert, referenceGroup); // contains all new or clone of SimpleParameterValue var paramValuesClone = new Dictionary <Guid, RequirementsContainerParameterValue>(); var unorderedReqContainer = new List <RequirementsContainer>(); // put all ParameterValueSet in transaction as update if existing, as created otherwise foreach (var group in allGroupInContext) { // Create SimpleParameterValue if missing RequirementsContainerParameterValue orderValue; if (!this.TryQueryOrderParameterOrCreate(group, out orderValue) && this.session.PermissionService.CanWrite(ClassKind.RequirementsContainerParameterValue, group)) { var cloneReq = group.Clone(false); cloneReq.ParameterValue.Add(orderValue); transaction.CreateOrUpdate(cloneReq); transaction.CreateOrUpdate(orderValue); paramValuesClone.Add(group.Iid, orderValue); // keep tab on unordered req to put order on their shortname unorderedReqContainer.Add(group); } else if (this.session.PermissionService.CanWrite(orderValue)) { var cloneValue = orderValue.Clone(false); if (group.Iid == groupToInsert.Iid) { // reset current value to max cloneValue.Value = new ValueArray <string>(new[] { int.MaxValue.ToString() }); } transaction.CreateOrUpdate(cloneValue); paramValuesClone.Add(group.Iid, cloneValue); } } // potentionally change container if they are different and update the container this.ChangeContainer(transaction, groupToInsert, referenceGroup); // keys unorderedReqContainer = unorderedReqContainer.OrderBy(x => x.ShortName).ToList(); var unorderedReqId = unorderedReqContainer.Select(x => x.Iid).ToList(); var orderedParamClone = paramValuesClone.Where(x => !unorderedReqId.Contains(x.Key)).OrderBy(x => this.getOrderKey(x.Value)).ToList(); orderedParamClone.AddRange(paramValuesClone.Where(x => unorderedReqId.Contains(x.Key)).OrderBy(x => unorderedReqId.IndexOf(x.Key))); paramValuesClone = orderedParamClone.ToDictionary(x => x.Key, x => x.Value); var reqOrderedUuid = paramValuesClone.Where(x => x.Key != groupToInsert.Iid).OrderBy(x => this.getOrderKey(x.Value)).Select(x => x.Key).ToList(); var indexOfRefInsertReq = reqOrderedUuid.IndexOf(referenceGroup.Iid); var endReqIdInterval = insertKind == InsertKind.InsertBefore ? indexOfRefInsertReq > 0 ? (Guid?)reqOrderedUuid[indexOfRefInsertReq - 1] : null : indexOfRefInsertReq < reqOrderedUuid.Count - 1 ? (Guid?)reqOrderedUuid[indexOfRefInsertReq + 1] : null; var upperPair = insertKind == InsertKind.InsertBefore ? paramValuesClone.First(x => x.Key == referenceGroup.Iid) : endReqIdInterval.HasValue ? paramValuesClone.First(x => x.Key == endReqIdInterval) : default(KeyValuePair <Guid, RequirementsContainerParameterValue>); var lowerPair = insertKind == InsertKind.InsertBefore ? endReqIdInterval.HasValue ? paramValuesClone.First(x => x.Key == endReqIdInterval) : default(KeyValuePair <Guid, RequirementsContainerParameterValue>) : paramValuesClone.First(x => x.Key == referenceGroup.Iid); var upperKey = upperPair.Value != null?this.getOrderKey(upperPair.Value) : int.MaxValue; var lowerKey = lowerPair.Value != null?this.getOrderKey(lowerPair.Value) : int.MinValue; // either just update key of the dropped requirement or recompute all keys var insertKey = this.ComputeOrderKey(lowerKey, upperKey, paramValuesClone.Values.Select(this.getOrderKey).ToList(), false); if (insertKey.HasValue) { paramValuesClone[groupToInsert.Iid].Value = new ValueArray <string>(new[] { insertKey.ToString() }); } else { // recompute all keys in ascending order var lower = 0; if (unorderedReqContainer.Any(x => !this.session.PermissionService.CanWrite(ClassKind.RequirementsContainerParameterValue, x))) { // if no permission to update everything, just update value with min or max depending on the kind of insert insertKey = this.ComputeOrderKey(lowerKey, upperKey, paramValuesClone.Values.Select(this.getOrderKey).ToList(), true); paramValuesClone[groupToInsert.Iid].Value = new ValueArray <string>(new[] { insertKey.ToString() }); } else { // have permission to update all keys, so do so foreach (var simpleParameterValue in paramValuesClone) { if (simpleParameterValue.Key == groupToInsert.Iid) { continue; } var key = this.ComputeOrderKey(lower, Int32.MaxValue, new List <int>(), true); if (simpleParameterValue.Key == referenceGroup.Iid && insertKind == InsertKind.InsertBefore) { var reqValueToInsert = paramValuesClone.First(x => x.Key == groupToInsert.Iid); reqValueToInsert.Value.Value = new ValueArray <string>(new[] { key.ToString() }); lower = key.Value; key = this.ComputeOrderKey(lower, Int32.MaxValue, new List <int>(), true); simpleParameterValue.Value.Value = new ValueArray <string>(new[] { key.ToString() }); lower = key.Value; } else if (simpleParameterValue.Key == referenceGroup.Iid && insertKind == InsertKind.InsertAfter) { simpleParameterValue.Value.Value = new ValueArray <string>(new[] { key.ToString() }); lower = key.Value; key = this.ComputeOrderKey(lower, Int32.MaxValue, new List <int>(), true); var reqValueToInsert = paramValuesClone.First(x => x.Key == groupToInsert.Iid); reqValueToInsert.Value.Value = new ValueArray <string>(new[] { key.ToString() }); lower = key.Value; } else { simpleParameterValue.Value.Value = new ValueArray <string>(new[] { key.ToString() }); lower = key.Value; } } } } return(transaction); }
/// <summary> /// Change the <see cref="IReadOnlyList{T}"/> within the current context /// </summary> /// <param name="reqContainerToInsert">The <see cref="RequirementsContainer"/> to insert</param> /// <param name="referenceGroup">The <see cref="RequirementsContainer"/> to insert before or after</param> protected abstract IReadOnlyList <RequirementsContainer> QueryAllContextReqContainer(RequirementsContainer reqContainerToInsert, RequirementsContainer referenceGroup);
/// <summary> /// Change the container of the <paramref name="reqContainerToInsert"/> to insert /// </summary> /// <param name="transaction">The current transaction</param> /// <param name="reqContainerToInsert">The <see cref="RequirementsContainer"/> to insert</param> /// <param name="referenceGroup">The <see cref="RequirementsContainer"/> to insert before or after</param> protected abstract void ChangeContainer(ThingTransaction transaction, RequirementsContainer reqContainerToInsert, RequirementsContainer referenceGroup);