mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-12-03 16:41:04 +01:00
d7b519a53c
Signed-off-by: Konstantina Chremmou <Konstantina.Chremmou@cloud.com>
1358 lines
43 KiB
C#
1358 lines
43 KiB
C#
/* Copyright (c) Cloud Software Group, Inc.
|
|
*
|
|
* 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<QueryFilter> queries = new List<QueryFilter>();
|
|
|
|
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<QueryFilter> subqueries = new List<QueryFilter>();
|
|
|
|
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<T> : QueryFilter
|
|
{
|
|
public readonly PropertyNames property;
|
|
private readonly Func<IXenObject, IComparable> 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<T> other = obj as PropertyQuery<T>;
|
|
if (other == null)
|
|
return false;
|
|
|
|
return other.property == this.property;
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return (int)property;
|
|
}
|
|
}
|
|
|
|
public abstract class RecursivePropertyQuery<T> : PropertyQuery<T>
|
|
{
|
|
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<T> : RecursivePropertyQuery<T> where T : XenObject<T>
|
|
{
|
|
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<T> : RecursivePropertyQuery<List<T>> where T : XenObject<T>
|
|
{
|
|
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<T> 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<String>
|
|
{
|
|
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<DateTime>
|
|
{
|
|
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<T> : PropertyQuery<T>
|
|
{
|
|
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<T> other = obj as EnumPropertyQuery<T>;
|
|
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<String>
|
|
{
|
|
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<T> : PropertyQuery<T> where T : XenObject<T>
|
|
{
|
|
/// <summary>
|
|
/// May be null, in which case this matches nothing.
|
|
/// </summary>
|
|
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<T>(Helpers.GetXmlAttribute(node, "query")));
|
|
}
|
|
else
|
|
{
|
|
query = XenConnection.FindByUUIDXenObject<T>(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<T> other = obj as XenModelObjectPropertyQuery<T>;
|
|
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<T> : PropertyQuery<List<T>>
|
|
{
|
|
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<T> 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<T> other = obj as ListContainsQuery<T>;
|
|
if (other == null)
|
|
return false;
|
|
|
|
return other.contains == this.contains;
|
|
}
|
|
|
|
public override abstract int GetHashCode();
|
|
}
|
|
|
|
public class XenModelObjectListContainsQuery<T> : ListContainsQuery<T> where T : XenObject<T>
|
|
{
|
|
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<T> other = obj as XenModelObjectListContainsQuery<T>;
|
|
if (other == null)
|
|
return false;
|
|
|
|
return other.queryUUID == this.queryUUID;
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return queryUUID.GetHashCode();
|
|
}
|
|
}
|
|
|
|
public class XenModelObjectListContainsNameQuery<T> : ListContainsQuery<T> where T : XenObject<T>
|
|
{
|
|
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<T> other = obj as XenModelObjectListContainsNameQuery<T>;
|
|
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<String>
|
|
{
|
|
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<T> : PropertyQuery<List<T>>
|
|
{
|
|
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<T> value)
|
|
{
|
|
return ((value.Count == 0) == empty);
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
if (!base.Equals(obj))
|
|
return false;
|
|
|
|
ListEmptyQuery<T> other = obj as ListEmptyQuery<T>;
|
|
if (other == null)
|
|
return false;
|
|
|
|
return other.empty == this.empty;
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return empty.GetHashCode();
|
|
}
|
|
}
|
|
|
|
public class IPAddressQuery : ListContainsQuery<ComparableAddress>
|
|
{
|
|
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<bool>
|
|
{
|
|
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<T> : PropertyQuery<T> where T : XenObject<T>
|
|
{
|
|
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<T> other = obj as NullQuery<T>;
|
|
if (other == null)
|
|
return false;
|
|
|
|
return query == other.query;
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return base.GetHashCode();
|
|
}
|
|
}
|
|
|
|
public class IntPropertyQuery : PropertyQuery<long>
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|