Add utility api for Tags usage

This commit is contained in:
Wojtek Figat
2022-12-20 22:39:11 +01:00
parent 0dd79fe10a
commit 2f6e793df4
4 changed files with 285 additions and 0 deletions

View File

@@ -38,6 +38,71 @@ Tag Tags::Get(const StringView& tagName)
return tag;
}
bool Tags::HasTag(const Array<Tag>& 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<Tag>& list, const Tag& tag)
{
if (tag.Index == -1)
return false;
return list.Contains(tag);
}
bool Tags::HasAny(const Array<Tag>& list, const Array<Tag>& tags)
{
for (const Tag& tag : tags)
{
if (HasTag(list, tag))
return true;
}
return false;
}
bool Tags::HasAnyExact(const Array<Tag>& list, const Array<Tag>& tags)
{
for (const Tag& tag : tags)
{
if (HasTagExact(list, tag))
return true;
}
return false;
}
bool Tags::HasAll(const Array<Tag>& list, const Array<Tag>& tags)
{
if (tags.IsEmpty())
return true;
for (const Tag& tag : tags)
{
if (!HasTag(list, tag))
return false;
}
return true;
}
bool Tags::HasAllExact(const Array<Tag>& list, const Array<Tag>& 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();

View File

@@ -113,4 +113,123 @@ namespace FlaxEngine
return Tags.Internal_GetTagName(Index);
}
}
partial class Tags
{
/// <summary>
/// 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.
/// </summary>
/// <param name="list">The tags list to use.</param>
/// <param name="tag">The tag to check.</param>
/// <returns>True if given tag is contained by the list of tags. Returns false for empty list.</returns>
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;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="list">The tags list to use.</param>
/// <param name="tag">The tag to check.</param>
/// <returns>True if given tag is contained by the list of tags. Returns false for empty list.</returns>
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;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="list">The tags list to use.</param>
/// <param name="tags">The tags to check.</param>
/// <returns>True if any of of the given tags is contained by the list of tags.</returns>
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;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="list">The tags list to use.</param>
/// <param name="tags">The tags to check.</param>
/// <returns>True if any of the given tags is contained by the list of tags.</returns>
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;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="list">The tags list to use.</param>
/// <param name="tags">The tags to check.</param>
/// <returns>True if all of the given tags are contained by the list of tags. Returns true for empty list.</returns>
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;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="list">The tags list to use.</param>
/// <param name="tags">The tags to check.</param>
/// <returns>True if all of the given tags are contained by the list of tags. Returns true for empty list.</returns>
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;
}
}
}

View File

@@ -79,6 +79,55 @@ API_CLASS(Static) class FLAXENGINE_API Tags
/// <returns>The tag.</returns>
API_FUNCTION() static Tag Get(const StringView& tagName);
public:
/// <summary>
/// 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.
/// </summary>
/// <param name="list">The tags list to use.</param>
/// <param name="tag">The tag to check.</param>
/// <returns>True if given tag is contained by the list of tags. Returns false for empty list.</returns>
static bool HasTag(const Array<Tag>& list, const Tag& tag);
/// <summary>
/// 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.
/// </summary>
/// <param name="list">The tags list to use.</param>
/// <param name="tag">The tag to check.</param>
/// <returns>True if given tag is contained by the list of tags. Returns false for empty list.</returns>
static bool HasTagExact(const Array<Tag>& list, const Tag& tag);
/// <summary>
/// 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.
/// </summary>
/// <param name="list">The tags list to use.</param>
/// <param name="tags">The tags to check.</param>
/// <returns>True if any of of the given tags is contained by the list of tags.</returns>
static bool HasAny(const Array<Tag>& list, const Array<Tag>& tags);
/// <summary>
/// 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.
/// </summary>
/// <param name="list">The tags list to use.</param>
/// <param name="tags">The tags to check.</param>
/// <returns>True if any of the given tags is contained by the list of tags.</returns>
static bool HasAnyExact(const Array<Tag>& list, const Array<Tag>& tags);
/// <summary>
/// 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.
/// </summary>
/// <param name="list">The tags list to use.</param>
/// <param name="tags">The tags to check.</param>
/// <returns>True if all of the given tags are contained by the list of tags. Returns true for empty list.</returns>
static bool HasAll(const Array<Tag>& list, const Array<Tag>& tags);
/// <summary>
/// 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.
/// </summary>
/// <param name="list">The tags list to use.</param>
/// <param name="tags">The tags to check.</param>
/// <returns>True if all of the given tags are contained by the list of tags. Returns true for empty list.</returns>
static bool HasAllExact(const Array<Tag>& list, const Array<Tag>& tags);
private:
API_FUNCTION(NoProxy) static const String& GetTagName(int32 tag);
};

View File

@@ -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 <ThirdParty/catch2/catch.hpp>
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<String>({ 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<String>({ 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<Tag> 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;
}
}