using System; using System.Collections.Generic; using System.Xml; using UnityEngine; using Ubisoft.Hotel.Localisation.Editor; namespace Ubisoft.Hotel.Package.Editor { /// /// Class responsible for defining an InfoPlist property, typically through HPV via vode (https://confluence.ubisoft.com/x/CrxNQg), /// so consumers can define InfoPlist properties easily. /// /// Besides simple properties an InfoPlist property can be a list of InfoPlist properties and a Dictionary of InfoPlist properties. /// Hotel's first approach to support this kind of properties was to use recursivity but this recursivity doesn't get along with /// serialisation and produced the following warning message: /// "Serialization depth limit 10 exceeded at 'Ubisoft.Hotel.Package.Editor::InfoPlistProperty.m_valueAsList'. There may be an object composition cycle in one or more of your serialized classes." /// This is the reason why InfoPlistProperty relies on InfoPlistItem, which only supports simple properties. /// This means that Hotel doesn't support a List/Dictionary of a List/Dictionary of InfoPlistProperty objects. /// In order to address this complex cases PlatformBuildProperty_iOS provide InfoPlist raw properties which allow you to provide a string /// with the exact paragraph that you need to add to the InfoPlist document. /// [Serializable] public class InfoPlistProperty { public const string ATTRIBUTE_ITEM = "m_item"; public const string ATTRIBUTE_VALUE_AS_LIST = "m_valueAsList"; [SerializeField] private InfoPlistItem m_item; [SerializeField] private List m_valueAsList; private InfoPlistProperty(InfoPlistItem item) { m_item = item; } public InfoPlistProperty(string key, string value) : this(new InfoPlistItem(key, value)) { } public InfoPlistProperty(string key, bool value) : this(new InfoPlistItem(key, value)) { } public InfoPlistProperty(string key, int value) : this(new InfoPlistItem(key, value)) { } public InfoPlistProperty(string key, float value) : this(new InfoPlistItem(key, value)) { } public InfoPlistProperty(string key, double value) : this(new InfoPlistItem(key, value)) { } public InfoPlistProperty(string key, List value) : this(new InfoPlistItem(key, value)) { } public InfoPlistProperty(string key, List value, bool useArrayType = true) : this(new InfoPlistItem(key, useArrayType ? InfoPlistItem.EType.Array : InfoPlistItem.EType.Dictionary, null)) { ValueAsList = value; } public InfoPlistProperty Clone() { InfoPlistProperty returnValue = new InfoPlistProperty(Item.Clone()); if (ValueAsList != null) { returnValue.ValueAsList = new List(); int count = ValueAsList.Count; for (int i = 0; i < count; i++) { returnValue.ValueAsList.Add(ValueAsList[i].Clone()); } } return returnValue; } private InfoPlistItem Item { get { return m_item; } set { m_item = value; } } public string Key { get { return Item.Key; } set { Item.Key = value; } } internal InfoPlistItem.EType Type { get { return Item.Type; } set { Item.Type = value; } } public string TypeAsString { get { return Type.ToString(); } } public string Value { get { return Item.Value; } set { Item.Value = value; } } public List ValueAsList { get { return m_valueAsList; } set { m_valueAsList = value; } } public List ValueAsLocalisedString { get { return Item.ValueAsLocalisedString; } set { Item.ValueAsLocalisedString = value; } } public bool IsLocalisedStringType() { return Item.IsLocalisedStringType(); } public bool IsValid() { bool returnValue = Item.IsValid(); if (returnValue && IsMultipleType()) { returnValue = IsValueAsListValid(); } return returnValue; } public string GetDebugIsValidDescription() { string returnValue = Item.GetDebugIsValidDescription(); if (IsMultipleType()) { string detail = null; if (!IsValueAsListValid()) { detail = $" {Debug.FormatTextInCodeContext("ValueAsList")} is not valid."; } if (ValueAsList != null) { int count = ValueAsList.Count; for (int i = 0; i < count; i++) { if (!ValueAsList[i].IsValid()) { detail += $" {ValueAsList[i].GetDebugIsValidDescription()}"; } } } if (!string.IsNullOrEmpty(detail)) { returnValue += detail; } } return returnValue; } public bool IsMultipleType() { return Item.IsMultipleType(); } private bool IsValueAsListValid() { bool returnValue = true; if (ValueAsList != null) { int count = ValueAsList.Count; for (int i = 0; i < count && returnValue; i++) { returnValue = ValueAsList[i].IsValid(); } } return returnValue; } public bool Validate() { bool returnValue = Item.Validate(); // Clear ValueAsList for single value properties in order to avoid misunderstandings if (IsMultipleType()) { // Multiple type properties use ValueAsList instead of Value. Value is set to empty to avoid misunderstandings if (!string.IsNullOrEmpty(Value)) { Value = ""; returnValue = false; } if (ValueAsLocalisedString != null) { ValueAsLocalisedString.Clear(); } if (ValueAsList != null) { int count = ValueAsList.Count; for (int i = 0; i < count; i++) { if (!ValueAsList[i].Validate()) { returnValue = false; } } } } else if (ValueAsList != null && ValueAsList.Count > 0) { ValueAsList.Clear(); returnValue = false; } return returnValue; } public XmlElement GetValueNode(XmlDocument xmlDocument) { XmlElement returnValue; switch (Type) { case InfoPlistItem.EType.Array: returnValue = xmlDocument.CreateElement("array"); if (ValueAsList != null) { int count = ValueAsList.Count; for (int i = 0; i < count; i++) { _ = returnValue.AppendChild(ValueAsList[i].GetValueNode(xmlDocument)); } } break; case InfoPlistItem.EType.Dictionary: returnValue = xmlDocument.CreateElement("dict"); if (ValueAsList != null) { XmlElement keyChild; int count = ValueAsList.Count; for (int i = 0; i < count; i++) { // Key keyChild = xmlDocument.CreateElement("key"); keyChild.InnerText = ValueAsList[i].Key; _ = returnValue.AppendChild(keyChild); // Value _ = returnValue.AppendChild(ValueAsList[i].GetValueNode(xmlDocument)); } } break; default: returnValue = Item.GetValueNode(xmlDocument); break; } return returnValue; } public override string ToString() { return "Property [Key: " + Key + " Value: " + Value + " Type: " + Type + "]"; } } }