Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 55 additions & 1 deletion Scripts/Editor/Utils/SOCollectionsContextMenuItems.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,60 @@ namespace BrunoMikoski.ScriptableObjectCollections
{
public static class SOCollectionsProjectContextMenus
{
[MenuItem("Tools/ScriptableObjectCollection/Generate All Static Access Files", priority = 2000)]
private static void GenerateAllStaticAccessFiles()
{
CollectionsRegistry.Instance.ReloadCollections();

int generatedCount = 0;
IReadOnlyList<ScriptableObjectCollection> collections = CollectionsRegistry.Instance.Collections;
for (int i = 0; i < collections.Count; i++)
{
ScriptableObjectCollection collection = collections[i];
if (!CodeGenerationUtility.DoesStaticFileForCollectionExist(collection))
continue;

CodeGenerationUtility.GenerateStaticCollectionScript(collection);
generatedCount++;
}

AssetDatabase.Refresh();
Debug.Log($"[SOC] Generated static access files for {generatedCount} collections.");
}

[MenuItem("Tools/ScriptableObjectCollection/Generate All Static Access Files", validate = true)]
private static bool Validate_GenerateAllStaticAccessFiles()
{
return !EditorApplication.isPlaying;
}


[MenuItem("Tools/ScriptableObjectCollection/Generate Indirect Access for All Collection", priority = 2000)]
private static void GenerateIndirectAccessToAllKnowCollection()
{
CollectionsRegistry.Instance.ReloadCollections();

int generatedCount = 0;
IReadOnlyList<ScriptableObjectCollection> collections = CollectionsRegistry.Instance.Collections;
for (int i = 0; i < collections.Count; i++)
{
ScriptableObjectCollection collection = collections[i];

CodeGenerationUtility.GenerateIndirectAccessForCollectionItemType(collection.GetItemType());
generatedCount++;
}

AssetDatabase.Refresh();
Debug.Log($"[SOC] Generated indirect access files for {generatedCount} collections.");
}

[MenuItem("Tools/ScriptableObjectCollection/Generate Indirect Access for All Collection", validate = true)]
private static bool Validate_GenerateIndirectAccessToAllKnowCollection()
{
return !EditorApplication.isPlaying;
}


[MenuItem("Assets/Move to Different Collection", true, priority = 10000)]
private static bool ValidateMoveToDifferentCollection()
{
Expand Down Expand Up @@ -95,4 +149,4 @@ private static void SelectCollection()
Selection.activeObject = socItem.Collection;
}
}
}
}
4 changes: 2 additions & 2 deletions Scripts/Runtime/Core/ScriptableObjectCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,11 @@ public ScriptableObject AddNew(Type itemType, string assetName = "")
string parentFolderPath = Path.Combine(assetPath, "Items" );
AssetDatabaseUtils.CreatePathIfDoesntExist(parentFolderPath);

string itemName = assetName;
string itemName = assetName.ToValidFilename();

if (string.IsNullOrEmpty(itemName))
{
itemName = $"{itemType.Name}";
itemName = $"{itemType.Name}".ToValidFilename();
}

string uniqueAssetPath = AssetDatabase.GenerateUniqueAssetPath(Path.Combine(parentFolderPath, itemName + ".asset"));
Expand Down
30 changes: 15 additions & 15 deletions Scripts/Runtime/Core/ScriptableObjectCollectionItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,7 @@ public class ScriptableObjectCollectionItem : ScriptableObject, IComparable<Scri
{
[SerializeField, HideInInspector]
private LongGuid guid;
public LongGuid GUID
{
get
{
if (guid.IsValid())
return guid;

GenerateNewGUID();
return guid;
}
}
public LongGuid GUID => guid;

[SerializeField, CollectionReferenceLongGuid]
private LongGuid collectionGUID;
Expand Down Expand Up @@ -68,6 +58,16 @@ public int Index
return cachedIndex;
}
}

#if UNITY_EDITOR
private void OnValidate()
{
if (!guid.IsValid())
{
GenerateNewGUID();
}
}
#endif

public void SetCollection(ScriptableObjectCollection collection)
{
Expand Down Expand Up @@ -108,8 +108,8 @@ public override bool Equals(object o)
ScriptableObjectCollectionItem other = o as ScriptableObjectCollectionItem;
if (other == null)
return false;

return ReferenceEquals(this, other);
return guid.IsValid() && other.guid.IsValid() && guid == other.guid;
}

public static bool operator==(ScriptableObjectCollectionItem left, ScriptableObjectCollectionItem right)
Expand All @@ -133,7 +133,7 @@ public override bool Equals(object o)

public override int GetHashCode()
{
return GUID.GetHashCode();
return guid.IsValid() ? guid.GetHashCode() : 0;
}
}
}
}
88 changes: 74 additions & 14 deletions Scripts/Runtime/Extensions/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,33 @@ public static class StringExtensions
"group", "into", "join", "let", "nameof", "notnull", "on", "orderby", "partial", "partial", "remove",
"select", "set", "unmanaged", "value", "var", "when", "where", "where", "with", "yield","values"
};

private static readonly char[] EXTRA_INVALID_FILE_CHARS = { '`' };

private static readonly string[] WINDOWS_RESERVED_NAMES =
{
"CON","PRN","AUX","NUL","COM1","COM2","COM3","COM4","COM5","COM6","COM7","COM8","COM9",
"LPT1","LPT2","LPT3","LPT4","LPT5","LPT6","LPT7","LPT8","LPT9"
};

public static string Sanitize(this string input)
{
input = input.StartingNumbersToWords();
// replace white spaces with undescore, then replace all invalid chars with empty string
IEnumerable<string> pascalCase =
INVALID_CHARS_RGX.Replace(WHITE_SPACE.Replace(input, "_"), string.Empty)
// split by underscores
.Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries)
// set first letter to uppercase
.Select(w => STARTS_WITH_LOWER_CASE_CHAR.Replace(w, m => m.Value.ToUpper()))
// replace second and all following upper case letters to lower if there is no next lower (ABC -> Abc)
.Select(w => FIRST_CHAR_FOLLOWED_BY_UPPER_CASES_ONLY.Replace(w, m => m.Value.ToLower()))
// set upper case the first lower case following a number (Ab9cd -> Ab9Cd)
.Select(w => LOWER_CASE_NEXT_TO_NUMBER.Replace(w, m => m.Value.ToUpper()))
// lower second and next upper case letters except the last if it follows by any lower (ABcDEf -> AbcDef)
.Select(w => UPPER_CASE_INSIDE.Replace(w, m => m.Value.ToLower()));
// Treat any non-alphanumeric sequence as a word separator
input = Regex.Replace(input, "[^a-zA-Z0-9]+", "_");

IEnumerable<string> pascalCase =
input
.Trim('_')
// split by underscores
.Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries)
// set first letter to uppercase
.Select(w => STARTS_WITH_LOWER_CASE_CHAR.Replace(w, m => m.Value.ToUpper()))
// replace second and all following upper case letters to lower if there is no next lower (ABC -> Abc)
.Select(w => FIRST_CHAR_FOLLOWED_BY_UPPER_CASES_ONLY.Replace(w, m => m.Value.ToLower()))
// set upper case the first lower case following a number (Ab9cd -> Ab9Cd)
.Select(w => LOWER_CASE_NEXT_TO_NUMBER.Replace(w, m => m.Value.ToUpper()))
// lower second and next upper case letters except the last if it follows by any lower (ABcDEf -> AbcDef)
.Select(w => UPPER_CASE_INSIDE.Replace(w, m => m.Value.ToLower()));

return string.Concat(pascalCase);
}
Expand Down Expand Up @@ -333,5 +343,55 @@ public static string GetProjectPath(this string absolutePath)
string relativePath = ToPathWithConsistentSeparators(Path.GetRelativePath(projectPath, absolutePath));
return relativePath;
}

public static bool IsValidFilename(this string value)
{
if (string.IsNullOrWhiteSpace(value))
return false;

char[] invalid = Path.GetInvalidFileNameChars();
if (value.IndexOfAny(invalid) >= 0)
return false;

if (value.IndexOfAny(EXTRA_INVALID_FILE_CHARS) >= 0)
return false;

if (value.EndsWith(" ") || value.EndsWith("."))
return false;

string stem = Path.GetFileNameWithoutExtension(value);
if (WINDOWS_RESERVED_NAMES.Any(r => r.Equals(stem, StringComparison.OrdinalIgnoreCase)))
return false;

return true;
}

public static string ToValidFilename(this string value, char replacement = '_')
{
if (string.IsNullOrEmpty(value))
return value;

if (value.IsValidFilename())
return value;

string safe = value;

foreach (char c in Path.GetInvalidFileNameChars())
safe = safe.Replace(c, replacement);

foreach (char c in EXTRA_INVALID_FILE_CHARS)
safe = safe.Replace(c, replacement);

safe = safe.TrimEnd(' ', '.');

string stem = Path.GetFileNameWithoutExtension(safe);
if (WINDOWS_RESERVED_NAMES.Any(r => r.Equals(stem, StringComparison.OrdinalIgnoreCase)))
safe = "_" + safe;

if (string.IsNullOrWhiteSpace(safe))
safe = "_";

return safe;
}
}
}