From 2f6e793df47db39b8baad0833c63284d92058ba9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 20 Dec 2022 22:39:11 +0100 Subject: [PATCH] Add utility api for `Tags` usage --- Source/Engine/Level/Tags.cpp | 65 ++++++++++++++++ Source/Engine/Level/Tags.cs | 119 ++++++++++++++++++++++++++++++ Source/Engine/Level/Tags.h | 49 ++++++++++++ Source/Engine/Tests/TestLevel.cpp | 52 +++++++++++++ 4 files changed, 285 insertions(+) diff --git a/Source/Engine/Level/Tags.cpp b/Source/Engine/Level/Tags.cpp index 83bb2e13e..398326885 100644 --- a/Source/Engine/Level/Tags.cpp +++ b/Source/Engine/Level/Tags.cpp @@ -38,6 +38,71 @@ Tag Tags::Get(const StringView& tagName) return tag; } +bool Tags::HasTag(const Array& list, const Tag& tag) +{ + if (tag.Index == -1) + return false; + const String& tagName = tag.ToString(); + for (const Tag& e : list) + { + const String& eName = e.ToString(); + if (e == tag || (eName.Length() > tagName.Length() + 1 && eName.StartsWith(tagName) && eName[tagName.Length()] == '.')) + return true; + } + return false; +} + +bool Tags::HasTagExact(const Array& list, const Tag& tag) +{ + if (tag.Index == -1) + return false; + return list.Contains(tag); +} + +bool Tags::HasAny(const Array& list, const Array& tags) +{ + for (const Tag& tag : tags) + { + if (HasTag(list, tag)) + return true; + } + return false; +} + +bool Tags::HasAnyExact(const Array& list, const Array& tags) +{ + for (const Tag& tag : tags) + { + if (HasTagExact(list, tag)) + return true; + } + return false; +} + +bool Tags::HasAll(const Array& list, const Array& tags) +{ + if (tags.IsEmpty()) + return true; + for (const Tag& tag : tags) + { + if (!HasTag(list, tag)) + return false; + } + return true; +} + +bool Tags::HasAllExact(const Array& list, const Array& tags) +{ + if (tags.IsEmpty()) + return true; + for (const Tag& tag : tags) + { + if (!HasTagExact(list, tag)) + return false; + } + return true; +} + const String& Tags::GetTagName(int32 tag) { return Tag(tag).ToString(); diff --git a/Source/Engine/Level/Tags.cs b/Source/Engine/Level/Tags.cs index 0fda5feab..cab4ff81a 100644 --- a/Source/Engine/Level/Tags.cs +++ b/Source/Engine/Level/Tags.cs @@ -113,4 +113,123 @@ namespace FlaxEngine return Tags.Internal_GetTagName(Index); } } + + partial class Tags + { + /// + /// Checks if the list of tags contains a given tag (including parent tags check). For example, HasTag({"A.B"}, "A") returns true, for exact check use HasTagExact. + /// + /// The tags list to use. + /// The tag to check. + /// True if given tag is contained by the list of tags. Returns false for empty list. + public static bool HasTag(Tag[] list, Tag tag) + { + if (tag.Index == -1) + return false; + string tagName = tag.ToString(); + foreach (Tag e in list) + { + string eName = e.ToString(); + if (e == tag || (eName.Length > tagName.Length + 1 && eName.StartsWith(tagName) && eName[tagName.Length] == '.')) + return true; + } + return false; + } + + /// + /// Checks if the list of tags contains a given tag (exact match). For example, HasTag({"A.B"}, "A") returns false, for parents check use HasTag. + /// + /// The tags list to use. + /// The tag to check. + /// True if given tag is contained by the list of tags. Returns false for empty list. + public static bool HasTagExact(Tag[] list, Tag tag) + { + if (tag.Index == -1) + return false; + if (list == null) + return false; + foreach (Tag e in list) + { + if (e == tag) + return true; + } + return false; + } + + /// + /// Checks if the list of tags contains any of the given tags (including parent tags check). For example, HasAny({"A.B", "C"}, {"A"}) returns true, for exact check use HasAnyExact. + /// + /// The tags list to use. + /// The tags to check. + /// True if any of of the given tags is contained by the list of tags. + public static bool HasAny(Tag[] list, Tag[] tags) + { + if (list == null) + return false; + foreach (Tag tag in list) + { + if (HasTag(list, tag)) + return true; + } + return false; + } + + /// + /// Checks if the list of tags contains any of the given tags (exact match). For example, HasAnyExact({"A.B", "C"}, {"A"}) returns false, for parents check use HasAny. + /// + /// The tags list to use. + /// The tags to check. + /// True if any of the given tags is contained by the list of tags. + public static bool HasAnyExact(Tag[] list, Tag[] tags) + { + if (list == null) + return false; + foreach (Tag tag in list) + { + if (HasTagExact(list, tag)) + return true; + } + return false; + } + + /// + /// Checks if the list of tags contains all of the given tags (including parent tags check). For example, HasAll({"A.B", "C"}, {"A", "C"}) returns true, for exact check use HasAllExact. + /// + /// The tags list to use. + /// The tags to check. + /// True if all of the given tags are contained by the list of tags. Returns true for empty list. + public static bool HasAll(Tag[] list, Tag[] tags) + { + if (tags == null || tags.Length == 0) + return true; + if (list == null || list.Length == 0) + return false; + foreach (Tag tag in list) + { + if (!HasTag(list, tag)) + return false; + } + return true; + } + + /// + /// Checks if the list of tags contains all of the given tags (exact match). For example, HasAllExact({"A.B", "C"}, {"A", "C"}) returns false, for parents check use HasAll. + /// + /// The tags list to use. + /// The tags to check. + /// True if all of the given tags are contained by the list of tags. Returns true for empty list. + public static bool HasAllExact(Tag[] list, Tag[] tags) + { + if (tags == null || tags.Length == 0) + return true; + if (list == null || list.Length == 0) + return false; + foreach (Tag tag in list) + { + if (!HasTagExact(list, tag)) + return false; + } + return true; + } + } } diff --git a/Source/Engine/Level/Tags.h b/Source/Engine/Level/Tags.h index 5616627c9..b52ee0063 100644 --- a/Source/Engine/Level/Tags.h +++ b/Source/Engine/Level/Tags.h @@ -79,6 +79,55 @@ API_CLASS(Static) class FLAXENGINE_API Tags /// The tag. API_FUNCTION() static Tag Get(const StringView& tagName); +public: + /// + /// Checks if the list of tags contains a given tag (including parent tags check). For example, HasTag({"A.B"}, "A") returns true, for exact check use HasTagExact. + /// + /// The tags list to use. + /// The tag to check. + /// True if given tag is contained by the list of tags. Returns false for empty list. + static bool HasTag(const Array& list, const Tag& tag); + + /// + /// Checks if the list of tags contains a given tag (exact match). For example, HasTag({"A.B"}, "A") returns false, for parents check use HasTag. + /// + /// The tags list to use. + /// The tag to check. + /// True if given tag is contained by the list of tags. Returns false for empty list. + static bool HasTagExact(const Array& list, const Tag& tag); + + /// + /// Checks if the list of tags contains any of the given tags (including parent tags check). For example, HasAny({"A.B", "C"}, {"A"}) returns true, for exact check use HasAnyExact. + /// + /// The tags list to use. + /// The tags to check. + /// True if any of of the given tags is contained by the list of tags. + static bool HasAny(const Array& list, const Array& tags); + + /// + /// Checks if the list of tags contains any of the given tags (exact match). For example, HasAnyExact({"A.B", "C"}, {"A"}) returns false, for parents check use HasAny. + /// + /// The tags list to use. + /// The tags to check. + /// True if any of the given tags is contained by the list of tags. + static bool HasAnyExact(const Array& list, const Array& tags); + + /// + /// Checks if the list of tags contains all of the given tags (including parent tags check). For example, HasAll({"A.B", "C"}, {"A", "C"}) returns true, for exact check use HasAllExact. + /// + /// The tags list to use. + /// The tags to check. + /// True if all of the given tags are contained by the list of tags. Returns true for empty list. + static bool HasAll(const Array& list, const Array& tags); + + /// + /// Checks if the list of tags contains all of the given tags (exact match). For example, HasAllExact({"A.B", "C"}, {"A", "C"}) returns false, for parents check use HasAll. + /// + /// The tags list to use. + /// The tags to check. + /// True if all of the given tags are contained by the list of tags. Returns true for empty list. + static bool HasAllExact(const Array& list, const Array& tags); + private: API_FUNCTION(NoProxy) static const String& GetTagName(int32 tag); }; diff --git a/Source/Engine/Tests/TestLevel.cpp b/Source/Engine/Tests/TestLevel.cpp index 830868a1d..0ed90ce45 100644 --- a/Source/Engine/Tests/TestLevel.cpp +++ b/Source/Engine/Tests/TestLevel.cpp @@ -1,7 +1,10 @@ // Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. #include "Engine/Core/Math/Vector3.h" +#include "Engine/Core/Types/String.h" +#include "Engine/Core/Types/StringView.h" #include "Engine/Level/LargeWorlds.h" +#include "Engine/Level/Tags.h" #include TEST_CASE("LargeWorlds") @@ -16,3 +19,52 @@ TEST_CASE("LargeWorlds") CHECK(origin == Vector3(0, 0, LargeWorlds::ChunkSize * 1)); } } + +TEST_CASE("Tags") +{ + SECTION("Tag") + { + auto prevTags = Tags::List; + + Tags::List = Array({ TEXT("A"), TEXT("A.1"), TEXT("B"), TEXT("B.1"), }); + + auto a = Tags::Get(TEXT("A")); + auto a1 = Tags::Get(TEXT("A.1")); + auto b = Tags::Get(TEXT("B")); + auto b1 = Tags::Get(TEXT("B.1")); + auto c = Tags::Get(TEXT("C")); + CHECK(a.Index == 0); + CHECK(a1.Index == 1); + CHECK(b.Index == 2); + CHECK(b1.Index == 3); + CHECK(c.Index == 4); + + Tags::List = prevTags; + } + + SECTION("Tags") + { + auto prevTags = Tags::List; + + Tags::List = Array({ TEXT("A"), TEXT("A.1"), TEXT("B"), TEXT("B.1"), }); + + auto a = Tags::Get(TEXT("A")); + auto a1 = Tags::Get(TEXT("A.1")); + auto b = Tags::Get(TEXT("B")); + auto b1 = Tags::Get(TEXT("B.1")); + auto c = Tags::Get(TEXT("C")); + + Array list = { a1, b1 }; + + CHECK(Tags::HasTag(list, Tag()) == false); + CHECK(Tags::HasTag(list, a1) == true); + CHECK(Tags::HasTag(list, a) == true); + CHECK(Tags::HasTag(list, c) == false); + + CHECK(Tags::HasTagExact(list, a1) == true); + CHECK(Tags::HasTagExact(list, a) == false); + CHECK(Tags::HasTagExact(list, c) == false); + + Tags::List = prevTags; + } +}