using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using UnityEditor;
using UnityEditor.Search;
using UnityEngine;
static class Example_QueryEngine_AddFilterFromAttribute
{
static List<MyObjectType> s_Data;
[MenuItem("Examples/QueryEngine/AddFiltersFromAttribute")]
public static void RunExample()
{
// Set up the query engine
var queryEngine = new QueryEngine<MyObjectType>();
queryEngine.AddFilter("id", myObj => myObj.id);
queryEngine.SetSearchDataCallback(myObj => new[] { myObj.id.ToString(), myObj.name });
SetupPropertyHandlers(queryEngine);
// Register filters with our own attributes
queryEngine.AddFiltersFromAttribute<MyObjectFilterAttribute, MyObjectFilterParameterTransformerAttribute>();
s_Data = new List<MyObjectType>()
{
new MyObjectType { id = 0, name = "Test 1", values = new float[] {2, 20, 42} },
new MyObjectType { id = 1, name = "Test 2", values = new float[] {16, 64, 128} },
new MyObjectType { id = 2, name = "Test 3", values = new float[] {123, 48, 20}, property = new Property("size", PropertyType.Integer, 64) },
new MyObjectType { id = 3, name = "Test 4", values = new float[] {98, 45, 1}, property = new Property("size", PropertyType.Integer, 128) },
new MyObjectType { id = 4, name = "Test 5", values = new float[] {3, 51, 78}, property = new Property("tag", PropertyType.String, "item") },
new MyObjectType { id = 5, name = "Test 6", values = new float[] {321, 58, 32}, property = new Property("tag", PropertyType.String, "car item") }
};
// Find all items with values at index 1 lower or equal to 32
var query = queryEngine.ParseQuery("v(1)<=32");
var filteredData = query.Apply(s_Data).ToList();
Debug.Assert(filteredData.Count == 1, $"There should be 1 item in the filtered list but found {filteredData.Count} items.");
Debug.Assert(filteredData.Contains(s_Data[0]), "The first item should be in the list.");
// Find all items with property size greater or equal to 128
query = queryEngine.ParseQuery("#size>=128");
filteredData = query.Apply(s_Data).ToList();
Debug.Assert(filteredData.Count == 1, $"There should be 1 item in the filtered list but found {filteredData.Count} items.");
Debug.Assert(filteredData.Contains(s_Data[3]), $"The item \"{s_Data[3].name}\" should be in the list.");
}
static void SetupPropertyHandlers(QueryEngine<MyObjectType> qe)
{
qe.AddOperatorHandler(":", (Property v, int number, StringComparison sc) => PropertyIntCompare(v, number, (f, r) => f.ToString().IndexOf(r.ToString()) != -1));
qe.AddOperatorHandler("=", (Property v, int number) => PropertyIntCompare(v, number, (f, r) => f == r));
qe.AddOperatorHandler("!=", (Property v, int number) => PropertyIntCompare(v, number, (f, r) => f != r));
qe.AddOperatorHandler("<=", (Property v, int number) => PropertyIntCompare(v, number, (f, r) => f <= r));
qe.AddOperatorHandler("<", (Property v, int number) => PropertyIntCompare(v, number, (f, r) => f < r));
qe.AddOperatorHandler(">", (Property v, int number) => PropertyIntCompare(v, number, (f, r) => f > r));
qe.AddOperatorHandler(">=", (Property v, int number) => PropertyIntCompare(v, number, (f, r) => f >= r));
qe.AddOperatorHandler(":", (Property v, string s, StringComparison sc) => PropertyStringCompare(v, s, (f, r) => f.IndexOf(r, sc) != -1));
qe.AddOperatorHandler("=", (Property v, string s, StringComparison sc) => PropertyStringCompare(v, s, (f, r) => string.Equals(f, r, sc)));
qe.AddOperatorHandler("!=", (Property v, string s, StringComparison sc) => PropertyStringCompare(v, s, (f, r) => !string.Equals(f, r, sc)));
qe.AddOperatorHandler("<=", (Property v, string s, StringComparison sc) => PropertyStringCompare(v, s, (f, r) => string.Compare(f, r, sc) <= 0));
qe.AddOperatorHandler("<", (Property v, string s, StringComparison sc) => PropertyStringCompare(v, s, (f, r) => string.Compare(f, r, sc) < 0));
qe.AddOperatorHandler(">", (Property v, string s, StringComparison sc) => PropertyStringCompare(v, s, (f, r) => string.Compare(f, r, sc) > 0));
qe.AddOperatorHandler(">=", (Property v, string s, StringComparison sc) => PropertyStringCompare(v, s, (f, r) => string.Compare(f, r, sc) >= 0));
}
static bool PropertyStringCompare(Property p, string s, Func<string, string, bool> comparer)
{
if (p.type != PropertyType.String)
return false;
return comparer((string)p.value, s);
}
static bool PropertyIntCompare(Property p, int number, Func<int, int, bool> comparer)
{
if (p.type != PropertyType.Integer)
return false;
return comparer((int)p.value, number);
}
// Define a filter for a "value" property
[MyObjectFilter("v", "ParseFilterValuesIndex")]
static float FilterValues(MyObjectType myObj, int index)
{
return myObj.values[index];
}
// Define the parameter transformer for the filter "FilterValues"
[MyObjectFilterParameterTransformer]
static int ParseFilterValuesIndex(string param)
{
if (int.TryParse(param, NumberStyles.Number, new NumberFormatInfo(), out var i))
return i;
return 0;
}
// Define a regular expression filter for the property "property".
[MyObjectFilter("#([\\w.]+)", true)]
static Property FilterProperty(MyObjectType myObj, string filterMatch)
{
if (myObj.property.name == filterMatch)
return myObj.property;
return Property.invalid;
}
// Create a new attribute in order to register filters of this type only
class MyObjectFilterAttribute : QueryEngineFilterAttribute
{
public MyObjectFilterAttribute(string token, string[] supportedOperators = null)
: base(token, supportedOperators) {}
public MyObjectFilterAttribute(string token, StringComparison options, string[] supportedOperators = null)
: base(token, options, supportedOperators) {}
public MyObjectFilterAttribute(string token, string paramTransformerFunction, string[] supportedOperators = null)
: base(token, paramTransformerFunction, supportedOperators) {}
public MyObjectFilterAttribute(string token, string paramTransformerFunction, StringComparison options, string[] supportedOperators = null)
: base(token, paramTransformerFunction, options, supportedOperators) {}
public MyObjectFilterAttribute(string token, bool useRegularExpression, string[] supportedOperators = null)
: base(token, useRegularExpression, supportedOperators) {}
public MyObjectFilterAttribute(string token, bool useRegularExpression, StringComparison options, string[] supportedOperators = null)
: base(token, useRegularExpression, options, supportedOperators) {}
public MyObjectFilterAttribute(string token, bool useRegularExpression, string paramTransformerFunction, string[] supportedOperators = null)
: base(token, useRegularExpression, paramTransformerFunction, supportedOperators) {}
public MyObjectFilterAttribute(string token, bool useRegularExpression, string paramTransformerFunction, StringComparison options, string[] supportedOperators = null)
: base(token, useRegularExpression, paramTransformerFunction, options, supportedOperators) {}
}
class MyObjectFilterParameterTransformerAttribute : QueryEngineParameterTransformerAttribute {}
enum PropertyType
{
None,
Integer,
String,
Integer_Array
}
struct Property
{
public string name { get; }
public PropertyType type { get; }
public object value { get; set; }
public Property(string name, PropertyType type, object value)
{
this.name = name;
this.type = type;
this.value = value;
}
public static Property invalid = new Property(string.Empty, PropertyType.None, null);
}
class MyObjectType
{
public int id { get; set; }
public string name { get; set; } = string.Empty;
public Vector2 position { get; set; } = Vector2.zero;
public bool active { get; set; }
public float[] values { get; set; } = new float[0];
public Property property { get; set; } = Property.invalid;
public override string ToString()
{
return $"({id}, {name}, ({position.x}, {position.y}), {active})";
}
}
}