Appendix
Copy
The Aras.Server.Core.Configurator.IStringNotationConverter interface requires its implementors to support the ConvertExpressionToStringNotation method. This method provides a String Notation value to a given Effectivity Expression object representation.
The Aras.Server.Core.Configurator.IStringNotationConverter interface definition is:
namespace Aras.Server.Core.Configurator { public interface IStringNotationConverter { string ConvertExpressionToStringNotation(ExpressionBase expression); } }
This section provides examples of customizing the Effectivity String Notation. These simple examples supplement the concepts detailed in sections 7.10.1 Customizing Effectivity String Notation on Effectivity Expression and 7.10.2 Customizing Unified Effectivity String Notation.
Custom Effectivity String Notation on Effectivity Expression
The default Effectivity String Notations Converter uses a conjunction word for each logical operator:
- AND for logical conjunction
- OR for logical disjunction
- NOT for logical complement
- && for logical conjunction
- || for logical disjunction
- ! for logical complement
For example:
((Factory = Munich AND Production Date >= 01/01/2020) OR(Factory = Detroit AND Production Date >= 06/01/2020)) AND
NOT(Body Type = Hatchback)
The customized String Notation will use the following characters instead of the conjunction words:
For example:
((Factory = Munich && Production Date >= 01/01/2020) ||(Factory = Detroit && Production Date >= 06/01/2020)) &&
!(Body Type = Hatchback)
ExpressionToShortStringNotationConverter Class Definition
To achieve this customization goal, a new ExpressionToShortStringNotationConverter class is defined representing the new Converter:
internal class ExpressionToShortStringNotationConverter : Aras.Server.Core.Configurator.IStringNotationConverter { private readonly IEnumerable<Aras.Server.Core.Configurator.Variable> _variablesContainer; private static readonly Dictionary<Type, Aras.Server.Core.Configurator.DataType> typeTranslator = new Dictionary<Type, Aras.Server.Core.Configurator.DataType> { { typeof(string), Aras.Server.Core.Configurator.DataType.String }, { typeof(int), Aras.Server.Core.Configurator.DataType.Int }, { typeof(DateTime), Aras.Server.Core.Configurator.DataType.DateTime } }; /// <summary> /// Initializes a new instance of the ExpressionToShortStringNotationConverter class with the defined variables container /// </summary> /// <param name="variablesContainer">Represents a list of “Aras.Server.Core.Configurator.Variable” objects, which will be used to get human-readable identifiers of Variables and/or Named-constants</param> public CustomExpressionToStringNotationConverter(IEnumerable<Aras.Server.Core.Configurator.Variable> variablesContainer) { if (variablesContainer == null) { throw new ArgumentNullException(“variablesContainer”); } this._variablesContainer = variablesContainer; } /// <summary> /// Converts an incoming “Aras.Server.Core.Configurator.ExpressionBase” Effectivity Expression object representation to a string notation and returns it. /// </summary> /// <param name="expression">An Effectivity Expression object representation</param> /// <returns>A effectivity string notation value</returns> public string ConvertExpressionToStringNotation(Aras.Server.Core.Configurator.ExpressionBase expression) { if (expression == null) { throw new ArgumentNullException(“expression”); } return TranslateToStringNotationImplementation(expression, null); } private string TranslateToStringNotationImplementation(Aras.Server.Core.Configurator.ExpressionBase expression, Aras.Server.Core.Configurator.ExpressionBase parentExpression) { if (expression.ExpressionType == Aras.Server.Core.Configurator.ExpressionType.Term) { Aras.Server.Core.Configurator.ExpressionTerm expressionTerm = expression as Aras.Server.Core.Configurator.ExpressionTerm; string variableId = expressionTerm.OperandLeft.GetId(); Aras.Server.Core.Configurator.Variable variable = _variablesContainer.FirstOrDefault(var => var.Id == variableId); string variableName = variable?.Name ?? variableId; string assignedVariableValue; Aras.Server.Core.Configurator.ITermOperand expressionTermOperandRight = expressionTerm.OperandRight; if (expressionTermOperandRight.OperandType == Aras.Server.Core.Configurator.TermOperandType.NamedConstant) { string namedConstantId = expressionTermOperandRight.GetId(); assignedVariableValue = variable?.Enum?.FindNamedConstantById(namedConstantId)?.Name ?? namedConstantId; } else if (expressionTermOperandRight.OperandType == Aras.Server.Core.Configurator.TermOperandType.Constant) { object constantValue = expressionTermOperandRight.GetValue(); Type constantType = constantValue.GetType(); Aras.Server.Core.Configurator.DataType constantDataType; if (!typeTranslator.TryGetValue(constantType, out constantDataType)) { throw new NotSupportedException(FormattableString.Invariant($"'{constantType.FullName}' constant data type is not supported as the right operand type for string notation conversion”)); } //'null’ as the first parameter of the ‘ToLocalString()’ method call indicates the incoming DateTime object shouldn’t be adjusted to any time zone, because it’s assumed ‘datetime’ always is returned in neutral format with UTC time zone assignedVariableValue = constantDataType != Aras.Server.Core.Configurator.DataType.DateTime ? Convert.ToString(constantValue, CultureInfo.InvariantCulture) : Aras.I18NUtils.DateTimeConverter.ToLocalString(null, (DateTime)constantValue); } else { throw new NotSupportedException(FormattableString.Invariant($"{expressionTerm.OperandRight.OperandType} is not supported as the right operand type for string notation conversion”)); } return string.Format(CultureInfo.InvariantCulture, "{0} {1} {2}", AddSquareBracketsIfNeed(variableName), GetAlgebraicTermOperatorNotation(expressionTerm.Operator), AddSquareBracketsIfNeed(assignedVariableValue)); } string expressionStringNotation = string.Empty; string expressionOperatorStrNotation = GetAlgebraicNotationByOperator(expression.ExpressionType); //we assume at-least-one, at-most-one, exactly-one expressions consists of terms(<EQ><variable id="" /><named-constant id="" /></EQ>) only here if (expression.ExpressionType == Aras.Server.Core.Configurator.ExpressionType.AtLeastOne || expression.ExpressionType == Aras.Server.Core.Configurator.ExpressionType.AtMostOne || expression.ExpressionType == Aras.Server.Core.Configurator.ExpressionType.ExactlyOne) { IEnumerable<string> innerTermsStringNotation = expression.InnerExpressions.Select(expr => TranslateToStringNotationImplementation(expr, expression)); return string.Format(CultureInfo.InvariantCulture, expressionOperatorStrNotation, string.Join(" | ", innerTermsStringNotation)); } foreach (Aras.Server.Core.Configurator.ExpressionBase innerExpression in expression.InnerExpressions) { string innerExpressionStrNotation = TranslateToStringNotationImplementation(innerExpression, expression); if (!string.IsNullOrEmpty(innerExpressionStrNotation)) { if (expression.ExpressionType == Aras.Server.Core.Configurator.ExpressionType.NOT) { expressionStringNotation += string.Format(CultureInfo.InvariantCulture, expressionOperatorStrNotation, innerExpressionStrNotation); } else if (expression.ExpressionType == Aras.Server.Core.Configurator.ExpressionType.IMPLICATION) { string[] ifThen = expressionOperatorStrNotation.Split(' '); bool isStringNotationEmpty = string.IsNullOrEmpty(expressionStringNotation); // implicationTemplate describes “IF {expr}" or “THEN {expr}". In case “IF”, whitespace after "{expr}" is required -"IF {expr} “ string implicationTemplate = isStringNotationEmpty ? "{0} {1} " : "{0} {1}"; expressionStringNotation += string.Format(CultureInfo.InvariantCulture, implicationTemplate, ifThen[isStringNotationEmpty ? 0 : 1], innerExpressionStrNotation); } else { expressionStringNotation += !string.IsNullOrEmpty(expressionStringNotation) ? expressionOperatorStrNotation + innerExpressionStrNotation : innerExpressionStrNotation; } } } return parentExpression != null && parentExpression.InnerExpressions.Count > 1 && parentExpression.ExpressionType != Aras.Server.Core.Configurator.ExpressionType.IMPLICATION && expression.ExpressionType != Aras.Server.Core.Configurator.ExpressionType.NOT ? "(" + expressionStringNotation + ")” : expressionStringNotation; } private static string AddSquareBracketsIfNeed(string name) { return !string.IsNullOrEmpty(name) && !System.Text.RegularExpressions.Regex.IsMatch(name, @"^[a-zA-Z0-9]+$") ? string.Format(CultureInfo.InvariantCulture, "[{0}]”, name) : name; } private static string GetAlgebraicNotationByOperator(Aras.Server.Core.Configurator.ExpressionType boolOperator) { string notation = string.Empty; switch (boolOperator) { case Aras.Server.Core.Configurator.ExpressionType.AND: notation = " && "; break; case Aras.Server.Core.Configurator.ExpressionType.OR: notation = " || "; break; case Aras.Server.Core.Configurator.ExpressionType.NOT: notation = "!({0})"; break; case Aras.Server.Core.Configurator.ExpressionType.IMPLICATION: notation = “IF THEN"; break; case Aras.Server.Core.Configurator.ExpressionType.AtLeastOne: notation = “AT-LEAST-ONE({0})"; break; case Aras.Server.Core.Configurator.ExpressionType.AtMostOne: notation = “AT-MOST-ONE({0})"; break; case Aras.Server.Core.Configurator.ExpressionType.ExactlyOne: notation = “EXACTLY-ONE({0})"; break; default: throw new NotSupportedException(boolOperator + " type is not supported for string notation conversion”); } return notation; } private static string GetAlgebraicTermOperatorNotation(Aras.Server.Core.Configurator.TermOperator termOperator) { string algebraicTermOperatorNotation; switch (termOperator) { case Aras.Server.Core.Configurator.TermOperator.Equal: algebraicTermOperatorNotation = "="; break; case Aras.Server.Core.Configurator.TermOperator.GreaterThanOrEqual: algebraicTermOperatorNotation = ">="; break; case Aras.Server.Core.Configurator.TermOperator.LessThanOrEqual: algebraicTermOperatorNotation = "<="; break; default: throw new NotSupportedException(FormattableString.Invariant($"'{termOperator}' term operator is not supported for string notation conversion”)); } return algebraicTermOperatorNotation; } } Custom Unified Effectivity String Notation
By default, a comma (,) is used to combine multiple Effectivities on a single Relationship. A customized String Notation uses the OR conjunction word instead of the comma.
A customized String Notation example
| Effective Item | Single Effectivity String Notations | Unified Effectivity String Notation | Unified Effectivity String Notation |
| Effective Item | Single Effectivity String Notations | Default | Customized |
| Engine | Factory = Detroit | (Factory = Detroit), (Factory = Munich) | (Factory = Detroit) OR (Factory = Munich) |
| Engine | Factory = Munich | (Factory = Detroit), (Factory = Munich) | (Factory = Detroit) OR (Factory = Munich) |
JoinExpressionStringNotation Method Definition
To achieve this customization goal, change the definition of the JoinExpressionStringNotation internal method of the effs_getExpressionStringNotation Method as follows:
internal virtual string JoinExpressionStringNotation(IEnumerable<string> expressionStringNotations) { if (!string.IsNullOrEmpty(expressionStringNotations.ElementAtOrDefault(1))) { expressionStringNotations = expressionStringNotations.Select( expressionStringNotation => "(" + expressionStringNotation + ")”); } return string.Join(" OR ", expressionStringNotations); }
This section provides examples of customizing BOM Reports. These simple examples supplement the concept described in section 8 Configuring BOM Reports.
Adding a New Column Using an Item Property
In this example, the Multilevel BOM Report is customized. Using the following procedure, the Unit column is added between the Quantity and AML Status columns to display the Unit property of Part Items:
- Search for the PE_multilevelReport Query Definition (QD) and open its item view for editing.
- Click Show Editor on the PE_multilevelReport QD sidebar.
- Click the Properties button on the top RootPart row. The Properties dialog box appears.
- Add the Unit property to the Properties dialog box and click Save.
- Repeat steps 3-4 on the Part row representing the Part Query Item.
- Click Save, Unlock & Close on the PE_multilevelReport QD toolbar.
- Search for the Multilevel BOM Report and open its Item view for editing.
- Go to the Stylesheet editor tab.
- Add the
<th>Unit</th>table header cell element after<th>Quantity</th>. - Add a table data cell element for the
unitproperty values after the one forquantity. - Click Apply on the Stylesheet editor tab.
- Click Done on the Multilevel BOM Report toolbar. The Unit column is added to the Multilevel BOM Report.
<td rowspan="{$rowCount}" width="120px"> <xsl:value-of select="unit"/></td>
Adding a New Column Using a Relationship Property
In this example, the BOM Costing Report is customized. Using the following procedure, the Reference Designator column is added as the last column to display the Reference Designator property of the Part BOM Relationship ItemType:
- Search for the PE_costingReport Query Definition (QD) and open its item view for editing.
- Click Show Editor on the PE_costingReport QD sidebar.
- Click the Properties button on the Part BOM row. The Properties dialog box appears.
- Add the Reference Designator property to the Properties dialog box and click Save.
- Click Save, Unlock & Close on the PE_costingReport QD toolbar.
- Search for the BOM Costing Report and open its Item view for editing.
- Go to the Stylesheet editor tab.
- Add the
<th>Reference Designator</th>table header cell element after<th>Cost Basis</th>. - Add a table data cell element for the
reference_designatorproperty values after the one forcost_basis. - Click Apply on the Stylesheet editor tab.
- Click Done on the BOM Costing Report toolbar. The Reference Designator column is added to the BOM Costing Report.
<td><xsl:value-of select="Relationships/Item[@alias='Part BOM’ and @info='parent’]/reference_designator"/></td>
rowspan attribute as follows:<td rowspan="{$rowCount}" width="120px”><xsl:value-of select="Relationships/Item[@alias='Part BOM’ and@info='parent’]/property_name"/></td>
Adding the Sequence Column
In this example, the BOM Costing Report is customized. Using the following procedure, the Sequence column is added as the last column to show the Sequence property of the Part BOM Relationship ItemType:
- Search for the BOM Costing Report and open its Item view for editing.
- Go to the Stylesheet editor tab.
- Add the
<th>Sequence</th>table header cell element after<th>Cost Basis</th>. - Add a table data cell element for the
sort_orderproperty values after the one forcost_basis. - Click Apply on the Stylesheet editor tab.
- Click Done on the BOM Costing Report toolbar. The Sequence column is added to the BOM Costing Report.
<td><xsl:value-of select="sort_order"/></td>
rowspan attribute as follows:<td rowspan="{$rowCount}" width="120px”><xsl:value-of select="sort_oder"/></td>