/* Copyright (c) Citrix Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, * with or without modification, are permitted provided * that the following conditions are met: * * * Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ using System; using System.Collections.Generic; using System.Globalization; using System.Xml; using XenAdmin.CustomFields; using XenAPI; using XenCenterLib; using XenAdmin.Core; using XenAdmin.Network; namespace XenAdmin.XenSearch { public abstract class QueryFilter { public abstract bool? Match(IXenObject o); public abstract QueryFilter GetSubQueryFor(PropertyNames property); #region Marshalling protected abstract void AddXmlAttributes(XmlDocument doc, XmlNode node); // overrides can return null in order not to get into the XML at all public virtual XmlNode ToXmlNode(XmlDocument doc) { XmlNode node = doc.CreateElement(SearchMarshalling.GetClassName(this)); AddXmlAttributes(doc, node); return node; } #endregion } // A dummy filter that doesn't contribute to the search public class DummyQuery : QueryFilter { public DummyQuery() : base() { } // don't need public TrueQuery(XmlNode node) because they should never get into the XML public override bool? Match(IXenObject o) { return null; } public override QueryFilter GetSubQueryFor(PropertyNames property) { return null; } protected override void AddXmlAttributes(XmlDocument doc, XmlNode node) { } public override XmlNode ToXmlNode(XmlDocument doc) { return null; // don't put these types in the XML } } public class GroupQuery : QueryFilter { public enum GroupQueryType { And, Or, Nor }; public readonly GroupQueryType type; public readonly QueryFilter[] subqueries; public GroupQuery(QueryFilter[] subqueries, GroupQueryType type) { this.subqueries = subqueries; this.type = type; } public override bool? Match(IXenObject o) { bool? result = null; switch (type) { case GroupQueryType.And: foreach (QueryFilter subquery in subqueries) { bool? b = subquery.Match(o); if (b == false) return false; if (b == true) result = true; } return result; case GroupQueryType.Or: foreach (QueryFilter subquery in subqueries) { bool? b = subquery.Match(o); if (b == true) return true; if (b == false) result = false; } return result; case GroupQueryType.Nor: foreach (QueryFilter subquery in subqueries) { bool? b = subquery.Match(o); if (b == true) return false; if (b == false) result = true; } return result; default: return result; } } public override QueryFilter GetSubQueryFor(PropertyNames property) { List queries = new List(); foreach (QueryFilter subquery in subqueries) { QueryFilter query = subquery.GetSubQueryFor(property); if (query != null) queries.Add(query); } return queries.Count > 0 ? new GroupQuery(queries.ToArray(), this.type) : null; } public GroupQuery(XmlNode node) { this.type = (GroupQueryType) Enum.Parse(typeof(GroupQueryType), Helpers.GetXmlAttribute(node, "type")); List subqueries = new List(); foreach (XmlNode child in node.ChildNodes) { QueryFilter query = (QueryFilter) SearchMarshalling.FromXmlNode(child); if (query != null) subqueries.Add(query); } this.subqueries = subqueries.ToArray(); } public override XmlNode ToXmlNode(XmlDocument doc) { XmlNode node = base.ToXmlNode(doc); foreach (QueryFilter child in subqueries) { XmlNode childNode = child.ToXmlNode(doc); if (childNode != null) node.AppendChild(childNode); } return node; } protected override void AddXmlAttributes(XmlDocument doc, XmlNode node) { SearchMarshalling.AddAttribute(doc, node, "type", type.ToString()); } public override bool Equals(object obj) { GroupQuery other = obj as GroupQuery; if (other == null) return false; if (other.type != this.type) return false; int i; for (i = 0; i < subqueries.Length; i++) { if (i >= other.subqueries.Length) return false; if (!subqueries[i].Equals(other.subqueries[i])) return false; } if(i < other.subqueries.Length) return false; return true; } public override int GetHashCode() { return (int) type; } } public abstract class CustomFieldQueryBase : QueryFilter { public readonly CustomFieldDefinition definition; public CustomFieldQueryBase(CustomFieldDefinition definition) { this.definition = definition; } public override QueryFilter GetSubQueryFor(PropertyNames property) { return null; } } // This should have been called CustomFieldStringQuery, but is still called CustomFieldQuery // for historical reasons (it's known by that name in some saved search XML files). public class CustomFieldQuery : CustomFieldQueryBase { public readonly String query; public readonly StringPropertyQuery.PropertyQueryType type; public CustomFieldQuery(CustomFieldDefinition definition, String query, StringPropertyQuery.PropertyQueryType type) : base(definition) { this.query = query; this.type = type; } public override bool? Match(IXenObject o) { String value = CustomFieldsManager.GetCustomFieldValue(o, definition) as String; if (value == null) return false; if (query == null) return true; return StringPropertyQuery.MatchString(value, query.ToLower(), type, false); } public CustomFieldQuery(XmlNode node) : base(new CustomFieldDefinition(node.FirstChild)) { this.query = Helpers.GetXmlAttribute(node, "query"); this.type = (StringPropertyQuery.PropertyQueryType) Enum.Parse(typeof(StringPropertyQuery.PropertyQueryType), Helpers.GetXmlAttribute(node, "type")); } protected override void AddXmlAttributes(XmlDocument doc, XmlNode node) { node.AppendChild(definition.ToXmlNode(doc)); SearchMarshalling.AddAttribute(doc, node, "query", query); SearchMarshalling.AddAttribute(doc, node, "type", type.ToString()); } public override bool Equals(object obj) { CustomFieldQuery other = obj as CustomFieldQuery; if (other == null) return false; return definition.Equals(other.definition) && query.Equals(other.query) && type == other.type; } public override int GetHashCode() { return definition.GetHashCode() * query.GetHashCode() * type.GetHashCode(); } } public class CustomFieldDateQuery : CustomFieldQueryBase { public readonly DateTime query; public readonly DatePropertyQuery.PropertyQueryType type; public CustomFieldDateQuery(CustomFieldDefinition definition, DateTime query, DatePropertyQuery.PropertyQueryType type) : base(definition) { this.query = query; this.type = type; } public override bool? Match(IXenObject o) { object value = CustomFieldsManager.GetCustomFieldValue(o, definition); if (!(value is DateTime)) return false; return DatePropertyQuery.MatchDate((DateTime)value, query, type, DateTime.Now); } public CustomFieldDateQuery(XmlNode node) : base(new CustomFieldDefinition(node.FirstChild)) { string queryString = Helpers.GetXmlAttribute(node, "query"); this.query = DateTime.ParseExact(queryString, "yyyyMMdd", CultureInfo.InvariantCulture); this.type = (DatePropertyQuery.PropertyQueryType) Enum.Parse(typeof(DatePropertyQuery.PropertyQueryType), Helpers.GetXmlAttribute(node, "type")); } protected override void AddXmlAttributes(XmlDocument doc, XmlNode node) { node.AppendChild(definition.ToXmlNode(doc)); SearchMarshalling.AddAttribute(doc, node, "query", query.ToString("yyyyMMdd", CultureInfo.InvariantCulture)); SearchMarshalling.AddAttribute(doc, node, "type", type.ToString()); } public override bool Equals(object obj) { CustomFieldDateQuery other = obj as CustomFieldDateQuery; if (other == null) return false; return definition.Equals(other.definition) && query.Equals(other.query) && type == other.type; } public override int GetHashCode() { return definition.GetHashCode() * query.GetHashCode() * type.GetHashCode(); } } public abstract class PropertyQuery : QueryFilter { public readonly PropertyNames property; private readonly Func propertyAccessor; internal readonly bool nullProtect; protected PropertyQuery(PropertyNames property) : this(property, true) { } protected PropertyQuery(PropertyNames property, bool nullProtect) { this.property = property; this.propertyAccessor = PropertyAccessors.Get(property); this.nullProtect = nullProtect; } protected PropertyQuery(XmlNode node) : this(node, true) { } protected PropertyQuery(XmlNode node, bool nullProtect) { this.property = (PropertyNames)Enum.Parse(typeof(PropertyNames), Helpers.GetXmlAttribute(node, "property")); this.propertyAccessor = PropertyAccessors.Get(property); this.nullProtect = nullProtect; } protected override void AddXmlAttributes(XmlDocument doc, XmlNode node) { SearchMarshalling.AddAttribute(doc, node, "property", property.ToString()); } public abstract bool? MatchProperty(T o); public override bool? Match(IXenObject o) { Object value = propertyAccessor(o); if (!(value is T) && nullProtect) return false; return MatchProperty((T)value); } public override QueryFilter GetSubQueryFor(PropertyNames property) { return this.property == property ? this : null; } public override bool Equals(object obj) { PropertyQuery other = obj as PropertyQuery; if (other == null) return false; return other.property == this.property; } public override int GetHashCode() { return (int)property; } } public abstract class RecursivePropertyQuery : PropertyQuery { public readonly QueryFilter subQuery; public RecursivePropertyQuery(PropertyNames property, QueryFilter subQuery) : base(property) { this.subQuery = subQuery; } public RecursivePropertyQuery(XmlNode node) : base(node) { subQuery = (QueryFilter)SearchMarshalling.FromXmlNode(node.FirstChild); } public override XmlNode ToXmlNode(XmlDocument doc) { XmlNode node = base.ToXmlNode(doc); XmlNode innerNode = subQuery.ToXmlNode(doc); node.AppendChild(innerNode); return node; } } public class RecursiveXMOPropertyQuery : RecursivePropertyQuery where T : XenObject { public RecursiveXMOPropertyQuery(PropertyNames property, QueryFilter subQuery) : base(property, subQuery) { } public RecursiveXMOPropertyQuery(XmlNode node) : base(node) { } public override bool? MatchProperty(T o) { return subQuery.Match(o); } } public class RecursiveXMOListPropertyQuery : RecursivePropertyQuery> where T : XenObject { public RecursiveXMOListPropertyQuery(PropertyNames property, QueryFilter subQuery) : base(property, subQuery) { } public RecursiveXMOListPropertyQuery(XmlNode node) : base(node) { } // Return true if *any* of the items in the list matches the inner query. // If matching for all the items on the list is indeterminate, we return // indeterminate (null) (e.g., the subquery is still "Select a filter..."). // But if the list is empty we return false (e.g., filter by Server and the // item doesn't use a Server). public override bool? MatchProperty(List list) { var seenFalse = false; var seenNull = false; foreach (var o in list) { if (o != null) { var b = subQuery.Match(o); switch (b) { case true: return true; case false: seenFalse = true; break; default: seenNull = true; break; } } } if (seenFalse) return false; if (seenNull) return null; return false; } } public class StringPropertyQuery : PropertyQuery { public enum PropertyQueryType { exactmatch, contains, startswith, endswith, notcontain }; public readonly String query; public readonly PropertyQueryType type; internal readonly bool caseSensitive; public StringPropertyQuery(PropertyNames property, String query, PropertyQueryType type, bool caseSensitive) : base(property) { this.query = query; this.type = type; this.caseSensitive = caseSensitive; } public StringPropertyQuery(XmlNode node) : base(node) { this.caseSensitive = SearchMarshalling.ParseBool(Helpers.GetXmlAttribute(node, "casesensitive")); this.query = Helpers.GetXmlAttribute(node, "query"); this.type = (PropertyQueryType)Enum.Parse(typeof(PropertyQueryType), Helpers.GetXmlAttribute(node, "type")); } protected override void AddXmlAttributes(XmlDocument doc, XmlNode node) { base.AddXmlAttributes(doc, node); SearchMarshalling.AddAttribute(doc, node, "casesensitive", caseSensitive); SearchMarshalling.AddAttribute(doc, node, "query", query); SearchMarshalling.AddAttribute(doc, node, "type", type.ToString()); } public override bool? MatchProperty(String parameter) { return MatchString(parameter, query, type, caseSensitive); } public static bool MatchString(String candidate, String query, PropertyQueryType type, bool caseSensitive) { if (String.IsNullOrEmpty(query)) return true; String stringValue = caseSensitive ? candidate : candidate.ToLowerInvariant(); String queryValue = caseSensitive ? query : query.ToLowerInvariant(); switch (type) { case PropertyQueryType.contains: return stringValue.Contains(queryValue); case PropertyQueryType.startswith: return stringValue.StartsWith(queryValue); case PropertyQueryType.endswith: return stringValue.EndsWith(queryValue); case PropertyQueryType.exactmatch: return stringValue.Equals(queryValue); case PropertyQueryType.notcontain: return !stringValue.Contains(queryValue); default: return false; } } public override bool Equals(object obj) { if (!base.Equals(obj)) return false; StringPropertyQuery other = obj as StringPropertyQuery; if (other == null) return false; return other.caseSensitive == this.caseSensitive && other.query == this.query && other.type == this.type; } public override int GetHashCode() { return query.GetHashCode(); } } public class DatePropertyQuery : PropertyQuery { public enum PropertyQueryType { today, yesterday, thisweek, lastweek, before, after, exact }; public readonly DateTime query; public readonly PropertyQueryType type; // For testing, it is necessary to be able to adjust the value of "now" private DateTime? pretendNow; public DateTime? PretendNow { set { pretendNow = value; } } public DateTime Now { get { return pretendNow.HasValue ? pretendNow.Value : DateTime.Now; } } public DatePropertyQuery(PropertyNames property, DateTime query, PropertyQueryType type) : base(property) { this.query = query; this.type = type; this.pretendNow = null; } public DatePropertyQuery(XmlNode node) : base(node) { string queryString = Helpers.GetXmlAttribute(node, "query"); if (queryString.Length == 8) // new style query = DateTime.ParseExact(queryString, "yyyyMMdd", CultureInfo.InvariantCulture); else if (Util.TryParseIso8601DateTime(queryString, out var result)) // old style query = result; else query = DateTime.MinValue; this.type = (PropertyQueryType)Enum.Parse(typeof(PropertyQueryType), Helpers.GetXmlAttribute(node, "type")); this.pretendNow = null; } protected override void AddXmlAttributes(XmlDocument doc, XmlNode node) { base.AddXmlAttributes(doc, node); SearchMarshalling.AddAttribute(doc, node, "query", query.ToString("yyyyMMdd", CultureInfo.InvariantCulture)); SearchMarshalling.AddAttribute(doc, node, "type", type.ToString()); } // Notes on the matching: // 1) The query is in local time, but the value is in UTC and needs to be // translated to local time for comparison. // 2) Because the user can only specify a date, times never play any part // in the matching. // 3) "Last week" means the day is between six days ago and today inclusive. // 4) "Before" and "after" include the day itself. public override bool? MatchProperty(DateTime value) { return MatchDate(value, query, type, Now); } public static bool? MatchDate(DateTime value, DateTime query, PropertyQueryType type, DateTime now) { DateTime valueLocal = value.ToLocalTime(); switch (type) { case PropertyQueryType.today: return now.Year == valueLocal.Year && now.DayOfYear == valueLocal.DayOfYear; case PropertyQueryType.yesterday: if (valueLocal.Year == now.Year) return (valueLocal.DayOfYear == now.DayOfYear - 1); else if (valueLocal.Year == now.Year - 1) return (now.DayOfYear == 1 && valueLocal.DayOfYear == DaysInYear(valueLocal.Year)); else return false; case PropertyQueryType.thisweek: if (valueLocal.Year == now.Year) { int diff = now.DayOfYear - valueLocal.DayOfYear; return (diff >= 0 && diff < 7); } else if (valueLocal.Year == now.Year - 1) { int diff = now.DayOfYear + DaysInYear(valueLocal.Year) - valueLocal.DayOfYear; return (diff >= 0 && diff < 7); } else return false; case PropertyQueryType.lastweek: if (valueLocal.Year == now.Year) { int diff = now.DayOfYear - valueLocal.DayOfYear; return (diff >= 7 && diff < 14); } else if (valueLocal.Year == now.Year - 1) { int diff = now.DayOfYear + DaysInYear(valueLocal.Year) - valueLocal.DayOfYear; return (diff >= 7 && diff < 14); } else return false; case PropertyQueryType.before: return valueLocal.Date <= query.Date; case PropertyQueryType.after: return valueLocal.Date >= query.Date; case PropertyQueryType.exact: return valueLocal.Date == query.Date; default: return false; } } private static int DaysInYear(int year) { return (DateTime.IsLeapYear(year) ? 366 : 365); } public override bool Equals(object obj) { if (!base.Equals(obj)) return false; DatePropertyQuery other = obj as DatePropertyQuery; if (other == null) return false; return other.type == this.type && other.query == this.query; } public override int GetHashCode() { return query.GetHashCode(); } } public class EnumPropertyQuery : PropertyQuery { public readonly T query; public readonly bool equals; public EnumPropertyQuery(PropertyNames property, T query, bool equals) : base(property) { this.query = query; this.equals = equals; System.Diagnostics.Trace.Assert(typeof(T).IsEnum); } public EnumPropertyQuery(XmlNode node) : base(node) { // Special case: "Type is SR" in legacy searches is now interpreted as // "Type is Remote SR". (The "type is" filter can't search for multiple types). if (typeof(T) == typeof(ObjectTypes) && Helpers.GetXmlAttribute(node, "query") == "SR") this.query = (T)Enum.Parse(typeof(T), "RemoteSR"); else this.query = (T)Enum.Parse(typeof(T), Helpers.GetXmlAttribute(node, "query")); this.equals = SearchMarshalling.ParseBool(Helpers.GetXmlAttribute(node, "equals")); } protected override void AddXmlAttributes(XmlDocument doc, XmlNode node) { base.AddXmlAttributes(doc, node); SearchMarshalling.AddAttribute(doc, node, "equals", equals); SearchMarshalling.AddAttribute(doc, node, "query", query.ToString()); } public override bool? MatchProperty(T value) { return value.Equals(query) == equals; } public override bool Equals(object obj) { if (!base.Equals(obj)) return false; EnumPropertyQuery other = obj as EnumPropertyQuery; if (other == null) return false; return other.query.Equals(this.query) && other.equals == this.equals; } public override int GetHashCode() { return query.GetHashCode(); } } public class ValuePropertyQuery : PropertyQuery { public readonly String query; public readonly bool equals; public ValuePropertyQuery(PropertyNames property, String query, bool equals) : base(property) { this.query = query; this.equals = equals; } public ValuePropertyQuery(XmlNode node) : base(node) { this.query = Helpers.GetXmlAttribute(node, "query"); this.equals = SearchMarshalling.ParseBool(Helpers.GetXmlAttribute(node, "equals")); } protected override void AddXmlAttributes(XmlDocument doc, XmlNode node) { base.AddXmlAttributes(doc, node); SearchMarshalling.AddAttribute(doc, node, "equals", equals); SearchMarshalling.AddAttribute(doc, node, "query", query.ToString()); } public override bool? MatchProperty(String value) { return value.Equals(query) == equals; } public override bool Equals(object obj) { if (!base.Equals(obj)) return false; ValuePropertyQuery other = obj as ValuePropertyQuery; if (other == null) return false; return other.equals == this.equals && other.query == this.query; } public override int GetHashCode() { return query.GetHashCode(); } } public class XenModelObjectPropertyQuery : PropertyQuery where T : XenObject { /// /// May be null, in which case this matches nothing. /// public readonly T query; public readonly bool equals; public XenModelObjectPropertyQuery(PropertyNames property, T query, bool equals) : base(property) { this.query = query; this.equals = equals; } public XenModelObjectPropertyQuery(XmlNode node) : base(node) { XmlAttribute uuid_node = node.Attributes["uuid"]; if (uuid_node == null) { // Just for backwards compat. query = XenConnection.FindByRef(new XenRef(Helpers.GetXmlAttribute(node, "query"))); } else { query = XenConnection.FindByUUIDXenObject(uuid_node.Value); } equals = SearchMarshalling.ParseBool(Helpers.GetXmlAttribute(node, "equals")); } protected override void AddXmlAttributes(XmlDocument doc, XmlNode node) { base.AddXmlAttributes(doc, node); SearchMarshalling.AddAttribute(doc, node, "equals", equals); SearchMarshalling.AddAttribute(doc, node, "uuid", GetUUID()); } private string GetUUID() { return query == null ? "invalid" : Helpers.GetUuid(query); } public override bool? MatchProperty(T value) { if (query == null) return false; return (Helpers.GetUuid(value) == Helpers.GetUuid(query)) == equals; } public override bool Equals(object obj) { if (!base.Equals(obj)) return false; XenModelObjectPropertyQuery other = obj as XenModelObjectPropertyQuery; if (other == null) return false; return other.equals == this.equals || Helpers.GetUuid(other.query) == Helpers.GetUuid(query); } public override int GetHashCode() { return query == null ? 0 : query.GetHashCode(); } } public abstract class ListContainsQuery : PropertyQuery> { public readonly bool contains; protected ListContainsQuery(PropertyNames property, bool contains) : base (property) { this.contains = contains; } protected ListContainsQuery(XmlNode node) : base (node) { this.contains = SearchMarshalling.ParseBool(Helpers.GetXmlAttribute(node, "contains")); } protected override void AddXmlAttributes(XmlDocument doc, XmlNode node) { base.AddXmlAttributes(doc, node); SearchMarshalling.AddAttribute(doc, node, "contains", contains); } public override sealed bool? MatchProperty(List value) { return contains == value.Exists(delegate(T t) { return MatchItem(t); }); } protected abstract bool MatchItem(T t); public override bool Equals(object obj) { if (!base.Equals(obj)) return false; ListContainsQuery other = obj as ListContainsQuery; if (other == null) return false; return other.contains == this.contains; } public override abstract int GetHashCode(); } public class XenModelObjectListContainsQuery : ListContainsQuery where T : XenObject { public readonly String queryUUID; public XenModelObjectListContainsQuery(PropertyNames property, T query, bool contains) : base(property, contains) { this.queryUUID = Helpers.GetUuid(query); } public XenModelObjectListContainsQuery(XmlNode node) : base(node) { this.queryUUID = Helpers.GetXmlAttribute(node, "uuid"); } protected override void AddXmlAttributes(XmlDocument doc, XmlNode node) { base.AddXmlAttributes(doc, node); SearchMarshalling.AddAttribute(doc, node, "uuid", queryUUID); } protected override bool MatchItem(T t) { return t != null && queryUUID == Helpers.GetUuid(t); } public override bool Equals(object obj) { if (!base.Equals(obj)) return false; XenModelObjectListContainsQuery other = obj as XenModelObjectListContainsQuery; if (other == null) return false; return other.queryUUID == this.queryUUID; } public override int GetHashCode() { return queryUUID.GetHashCode(); } } public class XenModelObjectListContainsNameQuery : ListContainsQuery where T : XenObject { public readonly string query; public readonly StringPropertyQuery.PropertyQueryType type; public XenModelObjectListContainsNameQuery(PropertyNames property, StringPropertyQuery.PropertyQueryType type, String query) : base(property, true) { this.query = query; this.type = type; } public XenModelObjectListContainsNameQuery(XmlNode node) : base(node) { this.query = Helpers.GetXmlAttribute(node, "query"); this.type = (StringPropertyQuery.PropertyQueryType) Enum.Parse(typeof(StringPropertyQuery.PropertyQueryType), Helpers.GetXmlAttribute(node, "type")); } protected override void AddXmlAttributes(XmlDocument doc, XmlNode node) { base.AddXmlAttributes(doc, node); SearchMarshalling.AddAttribute(doc, node, "query", query); SearchMarshalling.AddAttribute(doc, node, "type", type.ToString()); } protected override bool MatchItem(T value) { return StringPropertyQuery.MatchString(Helpers.GetName(value), query, type, false); } public override bool Equals(object obj) { if (!base.Equals(obj)) return false; XenModelObjectListContainsNameQuery other = obj as XenModelObjectListContainsNameQuery; if (other == null) return false; return other.query == this.query && other.type == this.type; } public override int GetHashCode() { return query.GetHashCode(); } } public class StringListContainsQuery : ListContainsQuery { public readonly String query; public StringListContainsQuery(PropertyNames property, String query, bool contains) : base(property, contains) { this.query = query; } public StringListContainsQuery(XmlNode node) : base(node) { this.query = Helpers.GetXmlAttribute(node, "query"); } protected override void AddXmlAttributes(XmlDocument doc, XmlNode node) { base.AddXmlAttributes(doc, node); SearchMarshalling.AddAttribute(doc, node, "query", query); } protected override bool MatchItem(String value) { return value != null && value.Equals(query); } public override bool Equals(object obj) { if (!base.Equals(obj)) return false; StringListContainsQuery other = obj as StringListContainsQuery; if (other == null) return false; return other.query.Equals(this.query); } public override int GetHashCode() { return query.GetHashCode(); } } public class ListEmptyQuery : PropertyQuery> { public readonly bool empty; public ListEmptyQuery(PropertyNames property, bool empty) : base(property) { this.empty = empty; } public ListEmptyQuery(XmlNode node) : base(node) { this.empty = SearchMarshalling.ParseBool(Helpers.GetXmlAttribute(node, "empty")); } protected override void AddXmlAttributes(XmlDocument doc, XmlNode node) { base.AddXmlAttributes(doc, node); SearchMarshalling.AddAttribute(doc, node, "empty", empty); } public override sealed bool? MatchProperty(List value) { return ((value.Count == 0) == empty); } public override bool Equals(object obj) { if (!base.Equals(obj)) return false; ListEmptyQuery other = obj as ListEmptyQuery; if (other == null) return false; return other.empty == this.empty; } public override int GetHashCode() { return empty.GetHashCode(); } } public class IPAddressQuery : ListContainsQuery { public readonly ComparableAddress address; public IPAddressQuery(PropertyNames property, ComparableAddress address) : base(property, true) { this.address = address; } public IPAddressQuery(XmlNode node) : base(node) { if (!ComparableAddress.TryParse(Helpers.GetXmlAttribute(node, "address"), true, true, out address)) throw new InvalidOperationException(String.Format("'{0}' is not a valid specification", address)); } protected override void AddXmlAttributes(XmlDocument doc, XmlNode node) { base.AddXmlAttributes(doc, node); SearchMarshalling.AddAttribute(doc, node, "address", address.ToString()); } protected override bool MatchItem(ComparableAddress t) { return address.Equals(t); } public override int GetHashCode() { return address.GetHashCode(); } public override bool Equals(object obj) { return obj is IPAddressQuery other && address.Equals(other.address); } } public class BooleanQuery : PropertyQuery { public readonly bool query; public BooleanQuery(PropertyNames property, bool query) : base(property) { this.query = query; } public BooleanQuery(XmlNode node) : base(node) { this.query = SearchMarshalling.ParseBool(Helpers.GetXmlAttribute(node, "query")); } protected override void AddXmlAttributes(XmlDocument doc, XmlNode node) { base.AddXmlAttributes(doc, node); SearchMarshalling.AddAttribute(doc, node, "query", query); } public override bool? MatchProperty(bool o) { return query == o; } public override bool Equals(object obj) { if (!base.Equals(obj)) return false; BooleanQuery other = obj as BooleanQuery; if (other == null) return false; return query == other.query; } public override int GetHashCode() { return base.GetHashCode(); } } public class NullQuery : PropertyQuery where T : XenObject { public readonly bool query; public NullQuery(PropertyNames property, bool query) : base(property, false) { this.query = query; } public NullQuery(XmlNode node) : base(node, false) { this.query = SearchMarshalling.ParseBool(Helpers.GetXmlAttribute(node, "query")); } protected override void AddXmlAttributes(XmlDocument doc, XmlNode node) { base.AddXmlAttributes(doc, node); SearchMarshalling.AddAttribute(doc, node, "query", query); } public override bool? MatchProperty(T o) { return ((o == null) == query); } public override bool Equals(object obj) { if (!base.Equals(obj)) return false; NullQuery other = obj as NullQuery; if (other == null) return false; return query == other.query; } public override int GetHashCode() { return base.GetHashCode(); } } public class IntPropertyQuery : PropertyQuery { public enum PropertyQueryType { exactmatch, gt, lt }; public readonly long query; public readonly PropertyQueryType type; public IntPropertyQuery(PropertyNames property, long query, PropertyQueryType type) : base(property) { this.query = query; this.type = type; } public IntPropertyQuery(XmlNode node) : base(node) { this.query = Int64.Parse(Helpers.GetXmlAttribute(node, "query")); this.type = (PropertyQueryType)Enum.Parse(typeof(PropertyQueryType), Helpers.GetXmlAttribute(node, "type")); } protected override void AddXmlAttributes(XmlDocument doc, XmlNode node) { base.AddXmlAttributes(doc, node); SearchMarshalling.AddAttribute(doc, node, "query", query.ToString()); SearchMarshalling.AddAttribute(doc, node, "type", type.ToString()); } public override bool? MatchProperty(long o) { switch (type) { case PropertyQueryType.exactmatch: return o == query; case PropertyQueryType.gt: return o > query; case PropertyQueryType.lt: return o < query; } return false; } public override bool Equals(object obj) { if (!base.Equals(obj)) return false; IntPropertyQuery other = obj as IntPropertyQuery; if (other == null) return false; return other.query == this.query && other.type == this.type; } public override int GetHashCode() { return (int)query; } } }