整合SLSUtilities
This commit is contained in:
@@ -0,0 +1,378 @@
|
||||
using LunaWolfStudiosEditor.ScriptableSheets.Shared;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace LunaWolfStudiosEditor.ScriptableSheets.Tables
|
||||
{
|
||||
public static class TableUtility
|
||||
{
|
||||
public static void FromFlatFileFormat(this Table<ITableProperty> propertyTable, string flatFileContent, FlatFileFormatSettings formatSettings)
|
||||
{
|
||||
var wrapper = formatSettings.GetWrapper();
|
||||
if (formatSettings.HasHeaders)
|
||||
{
|
||||
var firstRowEndIndex = flatFileContent.IndexOf(formatSettings.RowDelimiter);
|
||||
var joinedColumnHeaders = formatSettings.GetJoinedColumnHeaders(wrapper);
|
||||
var joinedColumnHeadersSanitized = SanitizeHeader(joinedColumnHeaders);
|
||||
if (firstRowEndIndex >= 0)
|
||||
{
|
||||
var headerRow = flatFileContent.Substring(0, firstRowEndIndex).Trim();
|
||||
var headerRowSanitized = SanitizeHeader(headerRow);
|
||||
// Validate column headers match the header row.
|
||||
if (joinedColumnHeadersSanitized.Contains(headerRowSanitized) || headerRowSanitized.Contains(joinedColumnHeadersSanitized))
|
||||
{
|
||||
flatFileContent = flatFileContent.Substring(firstRowEndIndex + formatSettings.RowDelimiter.Length);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Handle case where there's only a single row.
|
||||
var flatFileContentSanitized = SanitizeHeader(flatFileContent);
|
||||
if (joinedColumnHeadersSanitized.Contains(flatFileContentSanitized))
|
||||
{
|
||||
flatFileContent = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
var stringSplitOptions = formatSettings.RemoveEmptyRows ? StringSplitOptions.RemoveEmptyEntries : StringSplitOptions.None;
|
||||
string[] rowData;
|
||||
if (formatSettings.HasWrapping)
|
||||
{
|
||||
var inverseWrap = wrapper.WrapInverse(formatSettings.RowDelimiter);
|
||||
// Replace any trailing white space at end of line.
|
||||
flatFileContent = Regex.Replace(flatFileContent, $@"{Regex.Escape(wrapper.Close.ToString())}\s*{Regex.Escape(formatSettings.RowDelimiter)}\s*{Regex.Escape(wrapper.Open.ToString())}", inverseWrap);
|
||||
// Use new string array parameter to support older versions of C#.
|
||||
rowData = flatFileContent.Split(new string[] { inverseWrap }, stringSplitOptions);
|
||||
for (var i = 0; i < rowData.Length - 1; i++)
|
||||
{
|
||||
rowData[i] += wrapper.Close;
|
||||
var nextRow = i + 1;
|
||||
if (nextRow < rowData.Length)
|
||||
{
|
||||
rowData[nextRow] = wrapper.Open + rowData[nextRow];
|
||||
}
|
||||
}
|
||||
var totalWrapChars = flatFileContent.Count(c => c == wrapper.Open || c == wrapper.Close);
|
||||
var expectedWrapChars = rowData.Length * 2;
|
||||
if (totalWrapChars < expectedWrapChars)
|
||||
{
|
||||
Debug.LogWarning($"Invalid {nameof(WrapOption)} {formatSettings.WrapOption}. Found {totalWrapChars} matching char(s) but expected at least {expectedWrapChars}. Defaulting to {nameof(WrapOption)} {WrapOption.None}.");
|
||||
formatSettings.WrapOption = WrapOption.None;
|
||||
rowData = flatFileContent.Split(new string[] { formatSettings.RowDelimiter }, stringSplitOptions);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rowData = flatFileContent.Split(new string[] { formatSettings.RowDelimiter }, stringSplitOptions);
|
||||
}
|
||||
var rowIndex = 0;
|
||||
for (var row = formatSettings.FirstRowIndex; rowIndex < rowData.Length && row < propertyTable.Rows; row++)
|
||||
{
|
||||
string[] columnData;
|
||||
if (formatSettings.HasWrapping)
|
||||
{
|
||||
var line = rowData[rowIndex].Trim();
|
||||
columnData = line.Split(new string[] { wrapper.WrapInverse(formatSettings.ColumnDelimiter) }, StringSplitOptions.None);
|
||||
if (columnData.Length > 0 && columnData[0].StartsWith(wrapper.Open.ToString()))
|
||||
{
|
||||
columnData[0] = columnData[0].Remove(0, 1);
|
||||
var lastIndex = columnData.Length - 1;
|
||||
if (columnData[lastIndex].EndsWith(wrapper.Close.ToString()))
|
||||
{
|
||||
columnData[lastIndex] = columnData[lastIndex].Remove(columnData[lastIndex].Length - 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
columnData = rowData[rowIndex].Split(new string[] { formatSettings.ColumnDelimiter }, StringSplitOptions.None);
|
||||
}
|
||||
var columnIndex = 0;
|
||||
if (formatSettings.HasWrapping && formatSettings.EscapeOption != EscapeOption.None)
|
||||
{
|
||||
var escapedOpen = formatSettings.EscapeOption.GetEscapedWrapper(wrapper.Open, formatSettings.CustomEscapeSequence);
|
||||
var escapedClose = formatSettings.EscapeOption.GetEscapedWrapper(wrapper.Close, formatSettings.CustomEscapeSequence);
|
||||
for (var y = formatSettings.FirstColumnIndex; columnIndex < columnData.Length && y < propertyTable.Columns; y++)
|
||||
{
|
||||
var unescapedValue = wrapper.UnescapeContent(columnData[columnIndex], escapedOpen, escapedClose);
|
||||
propertyTable.UpdateProperty(row, y, unescapedValue, formatSettings);
|
||||
columnIndex++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var y = formatSettings.FirstColumnIndex; columnIndex < columnData.Length && y < propertyTable.Columns; y++)
|
||||
{
|
||||
propertyTable.UpdateProperty(row, y, columnData[columnIndex], formatSettings);
|
||||
columnIndex++;
|
||||
}
|
||||
}
|
||||
rowIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
private static ITableProperty UpdateProperty(this Table<ITableProperty> propertyTable, int row, int column, string value, FlatFileFormatSettings formatSettings)
|
||||
{
|
||||
var property = propertyTable.Get(row, column);
|
||||
// Property could be null when mixing array sizes.
|
||||
if (property != null)
|
||||
{
|
||||
property.SetProperty(value, formatSettings);
|
||||
}
|
||||
return property;
|
||||
}
|
||||
|
||||
public static void FromJsonFormat(this Table<ITableProperty> propertyTable, string json, JsonSerializationFormat format, FlatFileFormatSettings formatSettings)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case JsonSerializationFormat.Flat:
|
||||
propertyTable.FromJsonFlatFormat(json, formatSettings);
|
||||
break;
|
||||
|
||||
case JsonSerializationFormat.Hierarchy:
|
||||
propertyTable.FromJsonHierarchyFormat(json, formatSettings);
|
||||
break;
|
||||
|
||||
default:
|
||||
Debug.LogError($"Unsupported {nameof(JsonSerializationFormat)} {format}.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void FromJsonFlatFormat(this Table<ITableProperty> propertyTable, string json, FlatFileFormatSettings formatSettings)
|
||||
{
|
||||
try
|
||||
{
|
||||
var rowData = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, string>>>(json);
|
||||
var row = 0;
|
||||
foreach (var rowEntry in rowData)
|
||||
{
|
||||
if (row >= propertyTable.Rows)
|
||||
{
|
||||
LogJSONRowWarning(rowData.Count, propertyTable.Rows);
|
||||
break;
|
||||
}
|
||||
var column = formatSettings.FirstColumnIndex;
|
||||
var rowValues = rowEntry.Value.ToArray();
|
||||
for (var i = 0; i < rowValues.Length; i++)
|
||||
{
|
||||
if (column >= propertyTable.Columns)
|
||||
{
|
||||
// Add 1 for Actions column.
|
||||
Debug.LogWarning($"Mismatched columns. JSON data has ({rowEntry.Value.Count + 1}) column(s) but the property table only has ({propertyTable.Columns}) column(s).");
|
||||
break;
|
||||
}
|
||||
var property = propertyTable.UpdateProperty(row, column, rowValues[i].Value, formatSettings);
|
||||
// Only go to the next element if the previous was found successfully. This handles edge cases with mismatched array sizes.
|
||||
if (property == null)
|
||||
{
|
||||
i--;
|
||||
}
|
||||
else if (property.PropertyPath != rowValues[i].Key)
|
||||
{
|
||||
Debug.LogWarning($"Mismatched property path. Expected property path '{property.PropertyPath}' but used property path from JSON '{rowValues[i].Key}' for '{rowEntry.Key}'.");
|
||||
}
|
||||
column++;
|
||||
}
|
||||
row++;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Error parsing JSON {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void FromJsonHierarchyFormat(this Table<ITableProperty> propertyTable, string json, FlatFileFormatSettings formatSettings)
|
||||
{
|
||||
try
|
||||
{
|
||||
var rowData = JsonConvert.DeserializeObject<Dictionary<string, JObject>>(json);
|
||||
var row = 0;
|
||||
foreach (var rowEntry in rowData)
|
||||
{
|
||||
if (row >= propertyTable.Rows)
|
||||
{
|
||||
LogJSONRowWarning(rowData.Count, propertyTable.Rows);
|
||||
break;
|
||||
}
|
||||
var rootObject = propertyTable.Get(row, formatSettings.FirstColumnIndex).RootObject;
|
||||
var assetName = rowEntry.Key;
|
||||
if (assetName != rootObject.name)
|
||||
{
|
||||
var assetPath = AssetDatabase.GetAssetPath(rootObject);
|
||||
var assetRenameResponse = AssetDatabase.RenameAsset(assetPath, assetName);
|
||||
if (!string.IsNullOrEmpty(assetRenameResponse))
|
||||
{
|
||||
Debug.LogWarning($"Cannot rename {rootObject.name} at asset path {assetPath} to new name '{assetName.GetEscapedText()}'.\n{assetRenameResponse}");
|
||||
assetName = rootObject.name;
|
||||
}
|
||||
}
|
||||
var serializedObjectJson = JsonConvert.SerializeObject(rowEntry.Value);
|
||||
if (rootObject is ScriptableObject)
|
||||
{
|
||||
JsonUtility.FromJsonOverwrite(serializedObjectJson, rootObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorJsonUtility.FromJsonOverwrite(serializedObjectJson, rootObject);
|
||||
// Ensure m_Name property matches filename after deserializing.
|
||||
rootObject.name = assetName;
|
||||
}
|
||||
EditorUtility.SetDirty(rootObject);
|
||||
row++;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Error parsing JSON {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void LogJSONRowWarning(int expectedRows, int actualRows)
|
||||
{
|
||||
Debug.LogWarning($"Mismatched rows. JSON data has ({expectedRows}) row(s) but the property table only has ({actualRows}) row(s). Create ({expectedRows - actualRows}) more row(s).");
|
||||
}
|
||||
|
||||
public static string ToFlatFileFormat(this Table<ITableProperty> propertyTable, FlatFileFormatSettings formatSettings)
|
||||
{
|
||||
var lastColumn = propertyTable.Columns - 1;
|
||||
var lastRow = propertyTable.Rows - 1;
|
||||
var flatFile = new StringBuilder();
|
||||
var wrapper = formatSettings.GetWrapper();
|
||||
if (formatSettings.HasHeaders)
|
||||
{
|
||||
for (var column = formatSettings.FirstColumnIndex; column < formatSettings.ColumnHeaders.Length; column++)
|
||||
{
|
||||
var header = formatSettings.ColumnHeaders[column];
|
||||
if (formatSettings.HasWrapping)
|
||||
{
|
||||
flatFile.Wrap(header, wrapper);
|
||||
}
|
||||
else
|
||||
{
|
||||
flatFile.Append(header);
|
||||
}
|
||||
if (formatSettings.FirstColumnOnly)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (column < lastColumn)
|
||||
{
|
||||
flatFile.Append(formatSettings.ColumnDelimiter);
|
||||
}
|
||||
}
|
||||
if (propertyTable.Rows > formatSettings.FirstRowIndex)
|
||||
{
|
||||
flatFile.Append(formatSettings.RowDelimiter);
|
||||
}
|
||||
}
|
||||
for (var row = formatSettings.FirstRowIndex; row < propertyTable.Rows; row++)
|
||||
{
|
||||
for (var column = formatSettings.FirstColumnIndex; column < propertyTable.Columns; column++)
|
||||
{
|
||||
var property = propertyTable.Get(row, column)?.GetProperty(formatSettings);
|
||||
if (formatSettings.HasWrapping)
|
||||
{
|
||||
if (formatSettings.EscapeOption != EscapeOption.None)
|
||||
{
|
||||
var escapedOpen = formatSettings.EscapeOption.GetEscapedWrapper(wrapper.Open, formatSettings.CustomEscapeSequence);
|
||||
var escapedClose = formatSettings.EscapeOption.GetEscapedWrapper(wrapper.Close, formatSettings.CustomEscapeSequence);
|
||||
property = wrapper.EscapeContent(property, escapedOpen, escapedClose);
|
||||
}
|
||||
flatFile.Wrap(property, wrapper);
|
||||
}
|
||||
else
|
||||
{
|
||||
flatFile.Append(property);
|
||||
}
|
||||
if (formatSettings.FirstColumnOnly)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (column < lastColumn)
|
||||
{
|
||||
flatFile.Append(formatSettings.ColumnDelimiter);
|
||||
}
|
||||
}
|
||||
if (formatSettings.FirstRowOnly)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (row < lastRow)
|
||||
{
|
||||
flatFile.Append(formatSettings.RowDelimiter);
|
||||
}
|
||||
}
|
||||
return flatFile.ToString();
|
||||
}
|
||||
|
||||
public static string ToJsonFormat(this Table<ITableProperty> propertyTable, JsonSerializationFormat format, FlatFileFormatSettings formatSettings)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case JsonSerializationFormat.Flat:
|
||||
return propertyTable.ToJsonFlatFormat(formatSettings);
|
||||
|
||||
case JsonSerializationFormat.Hierarchy:
|
||||
return propertyTable.ToJsonHierarchyFormat();
|
||||
|
||||
default:
|
||||
Debug.LogError($"Unsupported {nameof(JsonSerializationFormat)} {format}.");
|
||||
break;
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private static string ToJsonFlatFormat(this Table<ITableProperty> propertyTable, FlatFileFormatSettings formatSettings)
|
||||
{
|
||||
var rowObjects = new Dictionary<string, Dictionary<string, string>>();
|
||||
for (var row = 0; row < propertyTable.Rows; row++)
|
||||
{
|
||||
if (propertyTable.Columns > formatSettings.FirstColumnIndex)
|
||||
{
|
||||
var rowRootObjectName = propertyTable.Get(row, formatSettings.FirstColumnIndex).RootObject.name;
|
||||
var rowData = new Dictionary<string, string>();
|
||||
for (var column = formatSettings.FirstColumnIndex; column < propertyTable.Columns; column++)
|
||||
{
|
||||
var value = propertyTable.Get(row, column);
|
||||
if (value != null)
|
||||
{
|
||||
rowData[value.PropertyPath] = value.GetProperty(formatSettings);
|
||||
}
|
||||
}
|
||||
rowObjects[rowRootObjectName] = rowData;
|
||||
}
|
||||
}
|
||||
return JsonConvert.SerializeObject(rowObjects, Formatting.Indented);
|
||||
}
|
||||
|
||||
private static string ToJsonHierarchyFormat(this Table<ITableProperty> propertyTable)
|
||||
{
|
||||
var rowObjects = new Dictionary<string, JObject>();
|
||||
for (var row = 0; row < propertyTable.Rows; row++)
|
||||
{
|
||||
if (propertyTable.Columns > 1)
|
||||
{
|
||||
var rowRootObject = propertyTable.Get(row, 1).RootObject;
|
||||
var unityJson = rowRootObject is ScriptableObject ? JsonUtility.ToJson(rowRootObject) : EditorJsonUtility.ToJson(rowRootObject);
|
||||
rowObjects[rowRootObject.name] = (JObject) JsonConvert.DeserializeObject(unityJson);
|
||||
}
|
||||
}
|
||||
return JsonConvert.SerializeObject(rowObjects, Formatting.Indented);
|
||||
}
|
||||
|
||||
private static string SanitizeHeader(string header)
|
||||
{
|
||||
return new string(header.Where(c => !char.IsWhiteSpace(c)).ToArray()).ToLower().Replace("m_", string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user