/// <summary> /// Validates the ContentFilter. /// </summary> /// <param name="context">The context.</param> /// <returns>The result of validation.</returns> public Result Validate(FilterContext context) { Result result = new Result(null); // check for empty filter. if (m_elements == null || m_elements.Count == 0) { return(result); } bool error = false; for (int ii = 0; ii < m_elements.Count; ii++) { ContentFilterElement element = m_elements[ii]; // check for null. if (element == null) { ServiceResult nullResult = ServiceResult.Create( StatusCodes.BadStructureMissing, "ContentFilterElement is null (Index={0}).", ii); result.ElementResults.Add(new ElementResult(nullResult)); error = true; continue; } element.Parent = this; // validate element. ElementResult elementResult = element.Validate(context, ii); if (ServiceResult.IsBad(elementResult.Status)) { result.ElementResults.Add(elementResult); error = true; continue; } result.ElementResults.Add(null); } // ensure the global error code. if (error) { result.Status = StatusCodes.BadContentFilterInvalid; } else { result.ElementResults.Clear(); } return(result); }
/// <summary> /// Finds the index of the specified element. /// </summary> /// <param name="target">The targetto be found.</param> /// <returns>The index of the specified element.</returns> private int FindElementIndex(ContentFilterElement target) { for (int ii = 0; ii < m_elements.Count; ii++) { if (Object.ReferenceEquals(target, m_elements[ii])) { return(ii); } } return(-1); }
/// <summary> /// Like FilterOperator /// </summary> private bool Like(FilterContext context, IFilterTarget target, ContentFilterElement element) { FilterOperand[] operands = GetOperands(element, 2); object firstOperand = GetValue(context, operands[0], target); string lhs; LocalizedText firstOperandLocalizedText = firstOperand as LocalizedText; if (firstOperandLocalizedText != null) { lhs = firstOperandLocalizedText.Text; } else { lhs = firstOperand as string; } object secondOperand = GetValue(context, operands[1], target); string rhs; LocalizedText secondOperandLocalizedText = secondOperand as LocalizedText; if (secondOperandLocalizedText != null) { rhs = secondOperandLocalizedText.Text; } else { rhs = secondOperand as string; } // this operator requires strings. if (lhs == null || rhs == null) { return false; } return Match((string)lhs, (string)rhs); }
/// <summary> /// InList FilterOperator /// </summary> private bool? InList(FilterContext context, IFilterTarget target, ContentFilterElement element) { FilterOperand[] operands = GetOperands(element, 0); object value = GetValue(context, operands[0], target); // check for a match. for (int ii = 1; ii < operands.Length; ii++) { object lhs = value; object rhs = GetValue(context, operands[ii], target); DoImplicitConversion(ref lhs, ref rhs); if (IsEqual(lhs, rhs)) { return true; } } // no match. return false; }
/// <summary> /// Between FilterOperator /// </summary> private bool? Between(FilterContext context, IFilterTarget target, ContentFilterElement element) { FilterOperand[] operands = GetOperands(element, 3); object value = GetValue(context, operands[0], target); object min = GetValue(context, operands[1], target); object max = GetValue(context, operands[2], target); // the min and max could be different data types so the implicit conversion must be done twice. object lhs = value; DoImplicitConversion(ref lhs, ref min); bool? result = null; if (lhs is IComparable && min is IComparable) { // check if never in range no matter what happens with the upper bound. if (((IComparable)lhs).CompareTo(min) < 0) { return false; } result = true; } lhs = value; DoImplicitConversion(ref lhs, ref max); if (lhs is IComparable && max is IComparable) { // check if never in range no matter what happens with the lower bound. if (((IComparable)lhs).CompareTo(max) > 0) { return false; } // can't determine if in range if lower bound could not be resolved. return result != null; } // return null if the types are not comparable. return null; }
/// <summary> /// LessThanOrEqual FilterOperator /// </summary> private bool? LessThanOrEqual(FilterContext context, IFilterTarget target, ContentFilterElement element) { FilterOperand[] operands = GetOperands(element, 2); object lhs = GetValue(context, operands[0], target); object rhs = GetValue(context, operands[1], target); DoImplicitConversion(ref lhs, ref rhs); if (lhs is IComparable && rhs is IComparable) { return ((IComparable)lhs).CompareTo(rhs) <= 0; } // return null if the types are not comparable. return null; }
/// <summary> /// Like FilterOperator /// </summary> private bool Like(FilterContext context, IFilterTarget target, ContentFilterElement element) { FilterOperand[] operands = GetOperands(element, 2); object lhs = GetValue(context, operands[0], target) as string; object rhs = GetValue(context, operands[1], target) as string; // this operator requires strings. if (lhs == null || rhs == null) { return false; } return Match((string)lhs, (string)rhs); }
/// <summary> /// RelatedTo FilterOperator /// </summary> private bool RelatedTo(FilterContext context, IFilterTarget target, ContentFilterElement element) { return RelatedTo(context, target, element, null); }
/// <summary> /// OfType FilterOperator /// </summary> private bool OfType(FilterContext context, IFilterTarget target, ContentFilterElement element) { FilterOperand[] operands = GetOperands(element, 1); // get the desired type. NodeId typeDefinitionId = GetValue(context, operands[0], target) as NodeId; if (typeDefinitionId == null || target == null) { return false; } // check the type. try { return target.IsTypeOf(context, typeDefinitionId); } catch { return false; } }
/// <summary> /// Returns the operands for the element. /// </summary> private FilterOperand[] GetOperands(ContentFilterElement element, int expectedCount) { FilterOperand[] operands = new FilterOperand[element.FilterOperands.Count]; int ii = 0; foreach (ExtensionObject extension in element.FilterOperands) { if (ExtensionObject.IsNull(extension)) { throw new ServiceResultException(StatusCodes.BadUnexpectedError, "FilterOperand is null."); } FilterOperand operand = extension.Body as FilterOperand; if (operand == null) { throw new ServiceResultException(StatusCodes.BadUnexpectedError, "FilterOperand is not supported."); } operands[ii++] = operand; } if (expectedCount > 0 && expectedCount != operands.Length) { throw new ServiceResultException(StatusCodes.BadUnexpectedError, "ContentFilterElement does not have the correct number of operands."); } return operands; }
/// <summary> /// Finds the index of the specified element. /// </summary> /// <param name="target">The targetto be found.</param> /// <returns>The index of the specified element.</returns> private int FindElementIndex(ContentFilterElement target) { for (int ii = 0; ii < m_elements.Count; ii++) { if (Object.ReferenceEquals(target, m_elements[ii])) { return ii; } } return -1; }
/// <summary> /// Pushes a new element onto the stack. /// </summary> /// <param name="op">The filter operator.</param> /// <param name="operands">The operands.</param> /// <returns></returns> public ContentFilterElement Push(FilterOperator op, params object[] operands) { // check if nothing more to do. if (operands == null || operands.Length == 0) { throw ServiceResultException.Create(StatusCodes.BadInvalidArgument, "ContentFilterElement does not have an operands."); } // create the element and set the operator. ContentFilterElement element = new ContentFilterElement(); element.FilterOperator = op; for (int ii = 0; ii < operands.Length; ii++) { // check if a FilterOperand was provided. FilterOperand filterOperand = operands[ii] as FilterOperand; if (filterOperand != null) { element.FilterOperands.Add(new ExtensionObject(filterOperand)); continue; } // check for reference to another ContentFilterElement. ContentFilterElement existingElement = operands[ii] as ContentFilterElement; if (existingElement != null) { int index = FindElementIndex(existingElement); if (index == -1) { throw ServiceResultException.Create(StatusCodes.BadInvalidArgument, "ContentFilterElement is not part of the ContentFilter."); } ElementOperand operand = new ElementOperand(); operand.Index = (uint)index; element.FilterOperands.Add(new ExtensionObject(operand)); continue; } // assume a literal operand. LiteralOperand literalOperand = new LiteralOperand(); literalOperand.Value = new Variant(operands[ii]); element.FilterOperands.Add(new ExtensionObject(literalOperand)); } // insert the new element at the begining of the list. m_elements.Insert(0, element); // re-number ElementOperands since all element were shifted up. for (int ii = 0; ii < m_elements.Count; ii++) { foreach (ExtensionObject extension in m_elements[ii].FilterOperands) { if (extension != null) { ElementOperand operand = extension.Body as ElementOperand; if (operand != null) { operand.Index++; } } } } // return new element. return element; }
/// <summary> /// Pushes a new element onto the stack. /// </summary> /// <param name="op">The filter operator.</param> /// <param name="operands">The operands.</param> /// <returns></returns> public ContentFilterElement Push(FilterOperator op, params object[] operands) { // check if nothing more to do. if (operands == null || operands.Length == 0) { throw ServiceResultException.Create(StatusCodes.BadInvalidArgument, "ContentFilterElement does not have an operands."); } // create the element and set the operator. ContentFilterElement element = new ContentFilterElement(); element.FilterOperator = op; for (int ii = 0; ii < operands.Length; ii++) { // check if a FilterOperand was provided. FilterOperand filterOperand = operands[ii] as FilterOperand; if (filterOperand != null) { element.FilterOperands.Add(new ExtensionObject(filterOperand)); continue; } // check for reference to another ContentFilterElement. ContentFilterElement existingElement = operands[ii] as ContentFilterElement; if (existingElement != null) { int index = FindElementIndex(existingElement); if (index == -1) { throw ServiceResultException.Create(StatusCodes.BadInvalidArgument, "ContentFilterElement is not part of the ContentFilter."); } ElementOperand operand = new ElementOperand(); operand.Index = (uint)index; element.FilterOperands.Add(new ExtensionObject(operand)); continue; } // assume a literal operand. LiteralOperand literalOperand = new LiteralOperand(); literalOperand.Value = new Variant(operands[ii]); element.FilterOperands.Add(new ExtensionObject(literalOperand)); } // insert the new element at the begining of the list. m_elements.Insert(0, element); // re-number ElementOperands since all element were shifted up. for (int ii = 0; ii < m_elements.Count; ii++) { foreach (ExtensionObject extension in m_elements[ii].FilterOperands) { if (extension != null) { ElementOperand operand = extension.Body as ElementOperand; if (operand != null) { operand.Index++; } } } } // return new element. return(element); }
/// <summary> /// IsNull FilterOperator /// </summary> private bool IsNull(FilterContext context, IFilterTarget target, ContentFilterElement element) { FilterOperand[] operands = GetOperands(element, 1); object rhs = GetValue(context, operands[0], target); if (rhs == null) { return true; } return false; }
/// <summary> /// Or FilterOperator /// </summary> private bool? Or(FilterContext context, IFilterTarget target, ContentFilterElement element) { FilterOperand[] operands = GetOperands(element, 2); bool? lhs = GetValue(context, operands[0], target) as bool?; // no need for further processing if first operand is true. if (lhs != null && lhs.Value) { return true; } bool? rhs = GetValue(context, operands[1], target) as bool?; if (lhs == null) { if (rhs == null || rhs == false) { return null; } else { return true; } } if (rhs == null) { if (lhs == null || lhs == false) { return null; } else { return true; } } return lhs.Value || rhs.Value; }
/// <summary> /// Cast FilterOperator /// </summary> private object Cast(FilterContext context, IFilterTarget target, ContentFilterElement element) { FilterOperand[] operands = GetOperands(element, 2); // get the value to cast. object value = GetValue(context, operands[0], target); if (value == null) { return null; } // get the datatype to cast to. NodeId datatype = GetValue(context, operands[1], target) as NodeId; if (datatype == null) { return null; } BuiltInType targetType = GetBuiltInType(datatype); // cast the value. return Cast(value, targetType); }
/// <summary> /// Not FilterOperator /// </summary> private bool? Not(FilterContext context, IFilterTarget target, ContentFilterElement element) { FilterOperand[] operands = GetOperands(element, 1); bool? rhs = GetValue(context, operands[0], target) as bool?; if (rhs == null) { return null; } return !rhs.Value; }
/// <summary> /// InView FilterOperator /// </summary> private bool InView(FilterContext context, IFilterTarget target, ContentFilterElement element) { // views only supported in advanced filter targets. IAdvancedFilterTarget advancedFilter = target as IAdvancedFilterTarget; if (advancedFilter == null) { return false; } FilterOperand[] operands = GetOperands(element, 1); // get the desired type. NodeId viewId = GetValue(context, operands[0], target) as NodeId; if (viewId == null || target == null) { return false; } // check the target. try { return advancedFilter.IsInView(context, viewId); } catch { return false; } }
/// <summary> /// Equals FilterOperator /// </summary> private bool Equals(FilterContext context, IFilterTarget target, ContentFilterElement element) { FilterOperand[] operands = GetOperands(element, 2); object lhs = GetValue(context, operands[0], target); object rhs = GetValue(context, operands[1], target); DoImplicitConversion(ref lhs, ref rhs); return IsEqual(lhs, rhs); }
/// <summary> /// RelatedTo FilterOperator /// </summary> private bool RelatedTo(FilterContext context, IFilterTarget target, ContentFilterElement element, NodeId intermediateNodeId) { // RelatedTo only supported in advanced filter targets. IAdvancedFilterTarget advancedTarget = target as IAdvancedFilterTarget; if (advancedTarget == null) { return false; } FilterOperand[] operands = GetOperands(element, 6); // get the type of the source. NodeId sourceTypeId = GetValue(context, operands[0], target) as NodeId; if (sourceTypeId == null) { return false; } // get the type of reference to follow. NodeId referenceTypeId = GetValue(context, operands[2], target) as NodeId; if (referenceTypeId == null) { return false; } // get the number of hops int? hops = 1; object hopsValue = GetValue(context, operands[3], target); if (hopsValue != null) { hops = Cast(hopsValue, BuiltInType.Int32) as int?; if (hops == null) { hops = 1; } } // get whether to include type definition subtypes. bool? includeTypeDefinitionSubtypes = true; object includeValue = GetValue(context, operands[4], target); if (includeValue != null) { includeTypeDefinitionSubtypes = Cast(includeValue, BuiltInType.Boolean) as bool?; if (includeTypeDefinitionSubtypes == null) { includeTypeDefinitionSubtypes = true; } } // get whether to include reference type subtypes. bool? includeReferenceTypeSubtypes = true; includeValue = GetValue(context, operands[5], target); if (includeValue != null) { includeReferenceTypeSubtypes = Cast(includeValue, BuiltInType.Boolean) as bool?; if (includeReferenceTypeSubtypes == null) { includeReferenceTypeSubtypes = true; } } NodeId targetTypeId = null; // check if elements are chained. ElementOperand chainedOperand = operands[1] as ElementOperand; if (chainedOperand != null) { if (chainedOperand.Index < 0 || chainedOperand.Index >= Elements.Count) { return false; } ContentFilterElement chainedElement = Elements[(int)chainedOperand.Index]; // get the target type from the first operand of the chained element. if (chainedElement.FilterOperator == FilterOperator.RelatedTo) { FilterOperand nestedType = ExtensionObject.ToEncodeable(chainedElement.FilterOperands[0]) as FilterOperand; targetTypeId = GetValue(context, nestedType, target) as NodeId; if (targetTypeId == null) { return false; } // find the nodes that meet the criteria in the first link of the chain. IList<NodeId> nodeIds = advancedTarget.GetRelatedNodes( context, intermediateNodeId, sourceTypeId, targetTypeId, referenceTypeId, hops.Value, includeTypeDefinitionSubtypes.Value, includeReferenceTypeSubtypes.Value); if (nodeIds == null || nodeIds.Count == 0) { return false; } // recursively follow the chain. for (int ii = 0; ii < nodeIds.Count; ii++) { // one match is all that is required. if (RelatedTo(context, target, chainedElement, nodeIds[ii])) { return true; } } // no matches. return false; } } // get the type of the target. if (targetTypeId == null) { targetTypeId = GetValue(context, operands[1], target) as NodeId; if (targetTypeId == null) { return false; } } // check the target. try { bool relatedTo = advancedTarget.IsRelatedTo( context, intermediateNodeId, sourceTypeId, targetTypeId, referenceTypeId, hops.Value, includeTypeDefinitionSubtypes.Value, includeReferenceTypeSubtypes.Value); return relatedTo; } catch { return false; } }
/// <summary> /// And FilterOperator /// </summary> private bool? And(FilterContext context, IFilterTarget target, ContentFilterElement element) { FilterOperand[] operands = GetOperands(element, 2); bool? lhs = GetValue(context, operands[0], target) as bool?; // no need for further processing if first operand is false. if (lhs != null && !lhs.Value) { return false; } bool? rhs = GetValue(context, operands[1], target) as bool?; if (lhs == null) { return (rhs == null)?null:(bool?)false; } if (rhs == null) { return (lhs == null)?null:(bool?)false; } return lhs.Value && rhs.Value; }