/// <summary> /// This constructor takes a Boolean expression in infix format. /// Some examples /// are shown below. Assume <b>T</b> is an instance of the /// <see cref="Tags"/> /// class. /// <list type="table"> /// <item> /// T[CommonPropertyNames.title] contains "foo" /// <term> /// </term> /// <description> /// <see cref="MediaComparer.IsMatch"/> will return /// true on media objects that have <i>foo</i> in the title. /// </description> /// </item> /// /// <item> /// T[CommonPropertyNames.creator] doesNotContain "\"HappyBand\"" /// <term> /// </term> /// <description> /// <see cref="MediaComparer.IsMatch"/> will return /// true on media objects that do not have <i>"HappyBand"</i> in the title. /// </description> /// </item> /// /// <item> /// (T[CommonPropertyNames.Class] = "object.item.audioItem.musicTrack" and Tags.PropertyAttributes.res_size > "40") or (T[CommonPropertyNames.author] exists true) /// <term> /// </term> /// <description> /// <see cref="MediaComparer.IsMatch"/> will return /// true on media objects that are music tracks with at least one resource greater than 40 bytes /// OR on media objects that have a value set for the author metadata. /// </description> /// </item> /// </list> /// </summary> /// <param name="infix">The boolean infix expression conforming to the syntax and semantics of ContentDirectory's boolean query language.</param> /// <exception cref="OpenSource.UPnP.AV.CdsMetadata.Error_MalformedSearchCriteria"> /// Thrown if the infix expression has a syntax error. /// </exception> public MediaComparer(string infix) { string allstring = infix.Trim(); if ((allstring == "") || (allstring == "*")) { this.m_AlwaysMatch = true; return; } //Initialize an empty stack and empty result string variable. // Stack stack = new Stack(); m_Postfix = new Queue(); RelExpStates state = RelExpStates.unknown; TokenResult token; while (infix.Length > 0) { infix = infix.Trim(); token = GetToken(ref infix, state); switch (state) { case RelExpStates.unknown: if (token.TokenType == Tokens.PropertyName) { state = RelExpStates.expectOp; } break; case RelExpStates.expectOp: if (token.TokenType != Tokens.Operator) { throw new UPnPCustomException(402, "Invalid Args: Invalid operator " + token.Data); } state = RelExpStates.expectValue; break; case RelExpStates.expectValue: if (token.TokenType != Tokens.PropertyValue) { throw new UPnPCustomException(402, "Invalid Args: Unexpected value " + token.Data); } state = RelExpStates.unknown; break; } switch (token.TokenType) { case Tokens.Operator: if (token.OpToken == OperatorTokens.LParen) { //left paren // stack.Push(token); } else if (token.OpToken == OperatorTokens.RParen) { //right paren // TokenResult tr = new TokenResult(false); do { if (stack.Count > 0) { tr = (TokenResult)stack.Pop(); if (tr.OpToken != OperatorTokens.LParen) { m_Postfix.Enqueue(tr); } } else { throw new UPnPCustomException(402, "Invalid Args: Missing Left Parenthesis."); } }while (tr.OpToken != OperatorTokens.LParen); } else { //standard operator // if (token.OpToken == OperatorTokens.Invalid) { throw new Exception("bad code"); } while ( (stack.Count > 0) && (((TokenResult)stack.Peek()).Precedence >= token.Precedence) && (((TokenResult)stack.Peek()).OpToken != OperatorTokens.LParen) ) { // While stack is not empty && // top operator has higher or equal precedence... // pop operator and stuff into queue m_Postfix.Enqueue(stack.Pop()); } stack.Push(token); } break; case Tokens.PropertyName: m_Postfix.Enqueue(token); TagExtractor te = new TagExtractor(token.Data); this.m_PE[token.Data] = te; break; case Tokens.PropertyValue: m_Postfix.Enqueue(token); break; } } // pop remaining items in stack and stuff into queue // while (stack.Count > 0) { TokenResult tr = (TokenResult)stack.Pop(); if (tr.OpToken != OperatorTokens.LParen) { m_Postfix.Enqueue(tr); } } }
/// <summary> /// Evaluates a subexpression of a whole expression. /// </summary> /// <param name="media">the media object with the metadata</param> /// <param name="prop">the property/attribute metadata to examine</param> /// <param name="op">the operator to use for examination</param> /// <param name="val">the value to compare against in string form</param> /// <returns></returns> /// <exception cref="OpenSource.UPnP.AV.CdsMetadata.Error_MalformedSearchCriteria"> /// Thrown when the expression provided at constructor time could not /// be used to evaluate the media because of syntax errors in /// the expression. /// </exception> private bool Evaluate(IUPnPMedia media, string prop, OperatorTokens op, string val) { bool result = false; TagExtractor te = (TagExtractor)this.m_PE[prop]; IList values = te.Extract(media); if (op == OperatorTokens.Exists) { bool testVal = (string.Compare(val, "true", true) == 0); result = ( ((values.Count > 0) && (testVal)) || ((values.Count == 0) && (testVal == false)) ); } else { foreach (object testVal in values) { int opCode = (int)op; if ((opCode >= (int)OperatorTokens.Equal) && (opCode <= (int)OperatorTokens.GreaterThanEqualTo)) { // Compare using a relational operator // try { int relResult = MetadataValueComparer.CompareTagValues(testVal, val, this.IgnoreCase); if (relResult == 0) { result = true; break; } } catch //(Exception e) { string opString = Enum.GetName(typeof(OperatorTokens), opCode); throw new Error_MalformedSearchCriteria("(" + val + ") " + opString + " " + testVal.ToString() + ") could not occur."); } } else if (op == OperatorTokens.Contains) { string tv = testVal.ToString(); int pos = tv.IndexOf(val); result = (pos >= 0); } else if (op == OperatorTokens.DoesNotContain) { string tv = testVal.ToString(); int pos = tv.IndexOf(val); result = (pos < 0); } else if (op == OperatorTokens.DerivedFrom) { string tv = testVal.ToString(); result = tv.StartsWith(val); } } } return(result); }
/// <summary> /// This constructor takes a Boolean expression in infix format. /// Some examples /// are shown below. Assume <b>T</b> is an instance of the /// <see cref="Tags"/> /// class. /// <list type="table"> /// <item> /// T[CommonPropertyNames.title] contains "foo" /// <term> /// </term> /// <description> /// <see cref="MediaComparer.IsMatch"/> will return /// true on media objects that have <i>foo</i> in the title. /// </description> /// </item> /// /// <item> /// T[CommonPropertyNames.creator] doesNotContain "\"HappyBand\"" /// <term> /// </term> /// <description> /// <see cref="MediaComparer.IsMatch"/> will return /// true on media objects that do not have <i>"HappyBand"</i> in the title. /// </description> /// </item> /// /// <item> /// (T[CommonPropertyNames.Class] = "object.item.audioItem.musicTrack" and Tags.PropertyAttributes.res_size > "40") or (T[CommonPropertyNames.author] exists true) /// <term> /// </term> /// <description> /// <see cref="MediaComparer.IsMatch"/> will return /// true on media objects that are music tracks with at least one resource greater than 40 bytes /// OR on media objects that have a value set for the author metadata. /// </description> /// </item> /// </list> /// </summary> /// <param name="infix">The boolean infix expression conforming to the syntax and semantics of ContentDirectory's boolean query language.</param> /// <exception cref="OpenSource.UPnP.AV.CdsMetadata.Error_MalformedSearchCriteria"> /// Thrown if the infix expression has a syntax error. /// </exception> public MediaComparer (string infix) { string allstring = infix.Trim(); if ((allstring == "") || (allstring == "*")) { this.m_AlwaysMatch = true; return; } //Initialize an empty stack and empty result string variable. // Stack stack = new Stack(); m_Postfix = new Queue(); RelExpStates state = RelExpStates.unknown; TokenResult token; while (infix.Length > 0) { infix = infix.Trim(); token = GetToken(ref infix, state); switch (state) { case RelExpStates.unknown: if (token.TokenType == Tokens.PropertyName) { state = RelExpStates.expectOp; } break; case RelExpStates.expectOp: if (token.TokenType != Tokens.Operator) { throw new UPnPCustomException(402, "Invalid Args: Invalid operator " + token.Data); } state = RelExpStates.expectValue; break; case RelExpStates.expectValue: if (token.TokenType != Tokens.PropertyValue) { throw new UPnPCustomException(402, "Invalid Args: Unexpected value " + token.Data); } state = RelExpStates.unknown; break; } switch (token.TokenType) { case Tokens.Operator: if (token.OpToken == OperatorTokens.LParen) { //left paren // stack.Push(token); } else if (token.OpToken == OperatorTokens.RParen) { //right paren // TokenResult tr = new TokenResult(false); do { if (stack.Count > 0) { tr = (TokenResult) stack.Pop(); if (tr.OpToken != OperatorTokens.LParen) { m_Postfix.Enqueue(tr); } } else { throw new UPnPCustomException(402, "Invalid Args: Missing Left Parenthesis."); } } while (tr.OpToken != OperatorTokens.LParen); } else { //standard operator // if (token.OpToken == OperatorTokens.Invalid) { throw new Exception("bad code"); } while ( (stack.Count > 0) && ( ((TokenResult) stack.Peek()).Precedence >= token.Precedence) && ( ((TokenResult) stack.Peek()).OpToken != OperatorTokens.LParen) ) { // While stack is not empty && // top operator has higher or equal precedence... // pop operator and stuff into queue m_Postfix.Enqueue( stack.Pop() ); } stack.Push(token); } break; case Tokens.PropertyName: m_Postfix.Enqueue(token); TagExtractor te = new TagExtractor(token.Data); this.m_PE[token.Data] = te; break; case Tokens.PropertyValue: m_Postfix.Enqueue(token); break; } } // pop remaining items in stack and stuff into queue // while (stack.Count > 0) { TokenResult tr = (TokenResult) stack.Pop(); if (tr.OpToken != OperatorTokens.LParen) { m_Postfix.Enqueue( tr ); } } }