Skip to content

Commit 1c60ceb

Browse files
authored
Merge pull request #186 from brunomikoski/feature/extra-checks-and-features
Feature/extra checks and features
2 parents 1ea45db + b808130 commit 1c60ceb

File tree

4 files changed

+146
-32
lines changed

4 files changed

+146
-32
lines changed

Scripts/Editor/Utils/SOCollectionsContextMenuItems.cs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,60 @@ namespace BrunoMikoski.ScriptableObjectCollections
66
{
77
public static class SOCollectionsProjectContextMenus
88
{
9+
[MenuItem("Tools/ScriptableObjectCollection/Generate All Static Access Files", priority = 2000)]
10+
private static void GenerateAllStaticAccessFiles()
11+
{
12+
CollectionsRegistry.Instance.ReloadCollections();
13+
14+
int generatedCount = 0;
15+
IReadOnlyList<ScriptableObjectCollection> collections = CollectionsRegistry.Instance.Collections;
16+
for (int i = 0; i < collections.Count; i++)
17+
{
18+
ScriptableObjectCollection collection = collections[i];
19+
if (!CodeGenerationUtility.DoesStaticFileForCollectionExist(collection))
20+
continue;
21+
22+
CodeGenerationUtility.GenerateStaticCollectionScript(collection);
23+
generatedCount++;
24+
}
25+
26+
AssetDatabase.Refresh();
27+
Debug.Log($"[SOC] Generated static access files for {generatedCount} collections.");
28+
}
29+
30+
[MenuItem("Tools/ScriptableObjectCollection/Generate All Static Access Files", validate = true)]
31+
private static bool Validate_GenerateAllStaticAccessFiles()
32+
{
33+
return !EditorApplication.isPlaying;
34+
}
35+
36+
37+
[MenuItem("Tools/ScriptableObjectCollection/Generate Indirect Access for All Collection", priority = 2000)]
38+
private static void GenerateIndirectAccessToAllKnowCollection()
39+
{
40+
CollectionsRegistry.Instance.ReloadCollections();
41+
42+
int generatedCount = 0;
43+
IReadOnlyList<ScriptableObjectCollection> collections = CollectionsRegistry.Instance.Collections;
44+
for (int i = 0; i < collections.Count; i++)
45+
{
46+
ScriptableObjectCollection collection = collections[i];
47+
48+
CodeGenerationUtility.GenerateIndirectAccessForCollectionItemType(collection.GetItemType());
49+
generatedCount++;
50+
}
51+
52+
AssetDatabase.Refresh();
53+
Debug.Log($"[SOC] Generated indirect access files for {generatedCount} collections.");
54+
}
55+
56+
[MenuItem("Tools/ScriptableObjectCollection/Generate Indirect Access for All Collection", validate = true)]
57+
private static bool Validate_GenerateIndirectAccessToAllKnowCollection()
58+
{
59+
return !EditorApplication.isPlaying;
60+
}
61+
62+
963
[MenuItem("Assets/Move to Different Collection", true, priority = 10000)]
1064
private static bool ValidateMoveToDifferentCollection()
1165
{
@@ -95,4 +149,4 @@ private static void SelectCollection()
95149
Selection.activeObject = socItem.Collection;
96150
}
97151
}
98-
}
152+
}

Scripts/Runtime/Core/ScriptableObjectCollection.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,11 @@ public ScriptableObject AddNew(Type itemType, string assetName = "")
133133
string parentFolderPath = Path.Combine(assetPath, "Items" );
134134
AssetDatabaseUtils.CreatePathIfDoesntExist(parentFolderPath);
135135

136-
string itemName = assetName;
136+
string itemName = assetName.ToValidFilename();
137137

138138
if (string.IsNullOrEmpty(itemName))
139139
{
140-
itemName = $"{itemType.Name}";
140+
itemName = $"{itemType.Name}".ToValidFilename();
141141
}
142142

143143
string uniqueAssetPath = AssetDatabase.GenerateUniqueAssetPath(Path.Combine(parentFolderPath, itemName + ".asset"));

Scripts/Runtime/Core/ScriptableObjectCollectionItem.cs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,7 @@ public class ScriptableObjectCollectionItem : ScriptableObject, IComparable<Scri
77
{
88
[SerializeField, HideInInspector]
99
private LongGuid guid;
10-
public LongGuid GUID
11-
{
12-
get
13-
{
14-
if (guid.IsValid())
15-
return guid;
16-
17-
GenerateNewGUID();
18-
return guid;
19-
}
20-
}
10+
public LongGuid GUID => guid;
2111

2212
[SerializeField, CollectionReferenceLongGuid]
2313
private LongGuid collectionGUID;
@@ -68,6 +58,16 @@ public int Index
6858
return cachedIndex;
6959
}
7060
}
61+
62+
#if UNITY_EDITOR
63+
private void OnValidate()
64+
{
65+
if (!guid.IsValid())
66+
{
67+
GenerateNewGUID();
68+
}
69+
}
70+
#endif
7171

7272
public void SetCollection(ScriptableObjectCollection collection)
7373
{
@@ -108,8 +108,8 @@ public override bool Equals(object o)
108108
ScriptableObjectCollectionItem other = o as ScriptableObjectCollectionItem;
109109
if (other == null)
110110
return false;
111-
112-
return ReferenceEquals(this, other);
111+
112+
return guid.IsValid() && other.guid.IsValid() && guid == other.guid;
113113
}
114114

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

134134
public override int GetHashCode()
135135
{
136-
return GUID.GetHashCode();
136+
return guid.IsValid() ? guid.GetHashCode() : 0;
137137
}
138138
}
139-
}
139+
}

Scripts/Runtime/Extensions/StringExtensions.cs

Lines changed: 74 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,23 +36,33 @@ public static class StringExtensions
3636
"group", "into", "join", "let", "nameof", "notnull", "on", "orderby", "partial", "partial", "remove",
3737
"select", "set", "unmanaged", "value", "var", "when", "where", "where", "with", "yield","values"
3838
};
39+
40+
private static readonly char[] EXTRA_INVALID_FILE_CHARS = { '`' };
41+
42+
private static readonly string[] WINDOWS_RESERVED_NAMES =
43+
{
44+
"CON","PRN","AUX","NUL","COM1","COM2","COM3","COM4","COM5","COM6","COM7","COM8","COM9",
45+
"LPT1","LPT2","LPT3","LPT4","LPT5","LPT6","LPT7","LPT8","LPT9"
46+
};
3947

4048
public static string Sanitize(this string input)
4149
{
42-
input = input.StartingNumbersToWords();
43-
// replace white spaces with undescore, then replace all invalid chars with empty string
44-
IEnumerable<string> pascalCase =
45-
INVALID_CHARS_RGX.Replace(WHITE_SPACE.Replace(input, "_"), string.Empty)
46-
// split by underscores
47-
.Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries)
48-
// set first letter to uppercase
49-
.Select(w => STARTS_WITH_LOWER_CASE_CHAR.Replace(w, m => m.Value.ToUpper()))
50-
// replace second and all following upper case letters to lower if there is no next lower (ABC -> Abc)
51-
.Select(w => FIRST_CHAR_FOLLOWED_BY_UPPER_CASES_ONLY.Replace(w, m => m.Value.ToLower()))
52-
// set upper case the first lower case following a number (Ab9cd -> Ab9Cd)
53-
.Select(w => LOWER_CASE_NEXT_TO_NUMBER.Replace(w, m => m.Value.ToUpper()))
54-
// lower second and next upper case letters except the last if it follows by any lower (ABcDEf -> AbcDef)
55-
.Select(w => UPPER_CASE_INSIDE.Replace(w, m => m.Value.ToLower()));
50+
// Treat any non-alphanumeric sequence as a word separator
51+
input = Regex.Replace(input, "[^a-zA-Z0-9]+", "_");
52+
53+
IEnumerable<string> pascalCase =
54+
input
55+
.Trim('_')
56+
// split by underscores
57+
.Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries)
58+
// set first letter to uppercase
59+
.Select(w => STARTS_WITH_LOWER_CASE_CHAR.Replace(w, m => m.Value.ToUpper()))
60+
// replace second and all following upper case letters to lower if there is no next lower (ABC -> Abc)
61+
.Select(w => FIRST_CHAR_FOLLOWED_BY_UPPER_CASES_ONLY.Replace(w, m => m.Value.ToLower()))
62+
// set upper case the first lower case following a number (Ab9cd -> Ab9Cd)
63+
.Select(w => LOWER_CASE_NEXT_TO_NUMBER.Replace(w, m => m.Value.ToUpper()))
64+
// lower second and next upper case letters except the last if it follows by any lower (ABcDEf -> AbcDef)
65+
.Select(w => UPPER_CASE_INSIDE.Replace(w, m => m.Value.ToLower()));
5666

5767
return string.Concat(pascalCase);
5868
}
@@ -333,5 +343,55 @@ public static string GetProjectPath(this string absolutePath)
333343
string relativePath = ToPathWithConsistentSeparators(Path.GetRelativePath(projectPath, absolutePath));
334344
return relativePath;
335345
}
346+
347+
public static bool IsValidFilename(this string value)
348+
{
349+
if (string.IsNullOrWhiteSpace(value))
350+
return false;
351+
352+
char[] invalid = Path.GetInvalidFileNameChars();
353+
if (value.IndexOfAny(invalid) >= 0)
354+
return false;
355+
356+
if (value.IndexOfAny(EXTRA_INVALID_FILE_CHARS) >= 0)
357+
return false;
358+
359+
if (value.EndsWith(" ") || value.EndsWith("."))
360+
return false;
361+
362+
string stem = Path.GetFileNameWithoutExtension(value);
363+
if (WINDOWS_RESERVED_NAMES.Any(r => r.Equals(stem, StringComparison.OrdinalIgnoreCase)))
364+
return false;
365+
366+
return true;
367+
}
368+
369+
public static string ToValidFilename(this string value, char replacement = '_')
370+
{
371+
if (string.IsNullOrEmpty(value))
372+
return value;
373+
374+
if (value.IsValidFilename())
375+
return value;
376+
377+
string safe = value;
378+
379+
foreach (char c in Path.GetInvalidFileNameChars())
380+
safe = safe.Replace(c, replacement);
381+
382+
foreach (char c in EXTRA_INVALID_FILE_CHARS)
383+
safe = safe.Replace(c, replacement);
384+
385+
safe = safe.TrimEnd(' ', '.');
386+
387+
string stem = Path.GetFileNameWithoutExtension(safe);
388+
if (WINDOWS_RESERVED_NAMES.Any(r => r.Equals(stem, StringComparison.OrdinalIgnoreCase)))
389+
safe = "_" + safe;
390+
391+
if (string.IsNullOrWhiteSpace(safe))
392+
safe = "_";
393+
394+
return safe;
395+
}
336396
}
337397
}

0 commit comments

Comments
 (0)