diff --git a/package/CHANGELOG.md b/package/CHANGELOG.md index f64af08fd..693c6f5d1 100755 --- a/package/CHANGELOG.md +++ b/package/CHANGELOG.md @@ -4,5 +4,5 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## [0.3.8-preview] - 2018-10-26 +## [0.4.0-preview] - 2018-11-12 Latest preview release. diff --git a/package/Documentation~/index.md b/package/Documentation~/index.md index 872dba4e9..40e68382b 100755 --- a/package/Documentation~/index.md +++ b/package/Documentation~/index.md @@ -1,11 +1,11 @@ # Unity Properties -Unity Properties are a set of interface and runtime implementation that offer a very fexible and efficient framework for data container description and visitation services. +Unity Properties are a set of interfaces and runtime implementation that offer a very fexible and efficient framework for data container description and visitation services. # About the Property API Use the Property API package to include the `Unity.Properties` namespace in your project. This namespace contains interfaces and classes you can use to: - * describe .Net types as property containers + * describe or wrap .Net types as property containers * resolve properties in nested property containers * implement visitors to traverse property containers and perform tasks (e.g. schema generation, serialization, UI generation, etc.) @@ -16,28 +16,341 @@ The Property API differs from the available .Net RTTI in multiple aspects: # Installing the Property API -To install this package, follow the instructions in the [Package Manager documentation](https://docs.unity3d.com/Packages/com.unity.package-manager-ui@latest/index.html). +To install this package, follow the instructions in the [Package Manager documentation](https://docs.unity3d.com/Packages/com.unity.package-manager-ui@latest/index.html). -# Using the Property API +# Code Generation -**TODO** +## Property Schema -# Technical details -## Requirements +You can use the `Unity.Properties.Codegen.Schema` API to build the representation of a property container type. This `Schema` object implements the `IPropertyContaner` interface, and can be serialized using the Property API. -This version of the Property API is compatible with the following versions of the Unity Editor: +By serializing it to JSON and saving it to a file ending with the `.properties` extension, you can use the `Unity.Properties.Codegen.SchemaImporter` to inspect the schema and generate C# code in the Unity Editor on demand. -* 2018.1 and later (recommended) +Here's a simple example: -To use this package, you must use the following project settings: -* .Net 4.x: To configure, open *Edit > Project Settings > Player*, and set the *Scripting Runtime Version* to *Experimental (.Net 4.x Equivalent*. Additionally, make sure *Api Compatibility Level* is set to *.Net 4.x*. +```csharp +using Unity.Properties.Codegen; +using Unity.Properties.Codegen.CSharp; +using Unity.Properties.Serialization; +using UnityEditor; -## Known limitations +namespace Sample +{ + internal static class SchemaCodeGeneration + { + [MenuItem("Properties/Generate")] + static void Generate() + { + var schema = new Schema(); + var myClass = new TypeNode() + { + Name = "MyClass", + Namespace = "Sample" + }; + myClass.Properties.Add(new PropertyNode() + { + Name = "IntValue", + PropertyType = PropertyType.Value, + ValueType = "int", + Documentation = "This is an int value" + }); + schema.Types.Add(myClass); -**TODO** + // Optional: + // save the schema using the `.properties` extension + // useful if you don't want to programmatically generate the schema object + File.WriteAllText("Assets/MyClass.properties", JsonSerializer.Serialize(schema)); -## Document revision history + // generate the C# code + // you can do the same thing by pressing the "Generate" button on the properties asset inspector + var builder = new CSharpSchemaBuilder(); + builder.Build("Assets/MyClass.Properties.cs", schema); + + AssetDatabase.Refresh(); + } + } +} +``` + +The serialized shema object looks like this: +```json +{ + "Types": [{ + "Namespace": "Sample", + "Name": "MyClass", + "Properties": [{ + "Name": "IntValue", + "ValueType": "int", + "PropertyType": "Value", + "Documentation": "This is an int value" + }] + }] +} +``` + +And the generated C# code looks like this. By default, the generated classes or structs are `public` and `partial`. This allows you to define your custom logic in a separate C# file. + +There are many options to control the C# code generation output. See the `Unity.Properties.Codegen.Schema` API documentation for details. + +```csharp +#if (NET_4_6 || NET_STANDARD_2_0) +using System; +using System.Collections.Generic; +using Unity.Properties; + +namespace Sample +{ + public partial class MyClass : IPropertyContainer + { + /// + /// property. + /// + public static ValueClassProperty IntValueProperty { get; private set; } + + private static ClassPropertyBag s_PropertyBag { get; set; } + + /// + public IPropertyBag PropertyBag => s_PropertyBag; + /// + public IVersionStorage VersionStorage => null; + + private static void InitializeProperties() + { + IntValueProperty = new ValueClassProperty( + "IntValue" + ,c => c.m_IntValue + ,(c, v) => c.m_IntValue = v + ); + } + + static partial void InitializeCustomProperties(); + + private static void InitializePropertyBag() + { + s_PropertyBag = new ClassPropertyBag( + IntValueProperty + ); + } + + static MyClass() + { + InitializeProperties(); + InitializeCustomProperties(); + InitializePropertyBag(); + } + + private int m_IntValue; + + /// + /// This is an int value + /// + public int IntValue + { + get { return IntValueProperty.GetValue(this); } + set { IntValueProperty.SetValue(this, value); } + } + } +} +#endif // (NET_4_6 || NET_STANDARD_2_0) +``` + +# Creating Property Containers + +To work with Properties, you need to instrument your .Net types with the `Unity.Properties.IPropertyContainer` interface. + +This interface declares a `Unity.Properties.IPropertyBag`, essentially exposing a list of properties for this particular type to consumers of the API. + +This manual instrumentation can be tedious (and error-prone!). Refer to the [Code Generation](#CodeGeneration) section for alternative code instrumentation methods. + +## Class Containers + +Property types available to `class` containers all have the `ClassProperty` suffix. + +```csharp +// Class container example instrumented with static properties +class MyClass : IPropertyContainer +{ + // Property declarations + // properties themselves can have any access modifier (public, internal, protected, private). + // They'll be available as long as they're added to the type's property bag (see below). + + // Value property types + public static ValueClassProperty SimpleValueProperty { get; private set; } + public static ClassValueClassProperty ClassContainerProperty { get; private set; } + public static StructValueClassProperty StructContainerProperty { get; private set; } + + // List property types + public static ValueListClassProperty SimpleValueListProperty { get; private set; } + public static ClassListClassProperty ListOfClassContainersProperty { get; private set; } + public static StructListClassProperty ListOfStructContainersProperty { get; private set; } + + // Backing Storage + // here we're using .Net fields hosted on the instance itself, but the data can come from anywhere + // For example, the data could come from a native (unmanaged) data store, or could live on another .Net object. + + private int m_SimpleValue; + private MyOtherClass m_ClassContainer; + private MyOtherStruct m_StructContainer; + private readonly List m_SimpleValueList = new List(); + private readonly List m_ListOfClassContainers = new List(); + private readonly List m_ListOfStructContainers = new List(); + + // Setup + + private static void InitializeProperties() + { + // All property types use delegates to access backing storage while preserving encapsulation + // The delegates here are anonymous and static, and will not allocate. + + // You can put anything in a Value property, as long as the algorithms support the value type. + // Algorithms built on top of Properties are encouraged to support all .Net primitive types, including + // enum types. + + SimpleValueProperty = new ValueClassProperty( + "SimpleValue" + , c => c.m_SimpleValue // read instance data (get) + , (c, v) => c.m_SimpleValue = v // write instance data (set) + ); + + // Use ClassContainer or StructContainer properties to recurse into complex types while visiting + // Note that ClassContainerProperty does not handle cyclic references automatically. + + ClassContainerProperty = new ClassValueClassProperty( + "ClassContainer" + , c => c.m_ClassContainer + , (c, v) => c.m_ClassContainer = v + ); + + StructContainerProperty = new StructValueClassProperty( + "StructContainer" + , c => c.m_StructContainer + , (c, v) => c.m_StructContainer = v + ); + + // List properties can be used to represent ordered collections of items + // Lists must be backed by a type that implements the `System.Collections.Generic.IList` interface. + + SimpleValueListProperty = new ValueListClassProperty( + "SimpleValueList" + , c => c.m_SimpleValueList // get list + ); + + // The 2nd delegate is optional, and defaults to use `default(TValue)` when adding a new item. + // You usually want to provide an item creation delegate when working with `class` value, otherwise new + // items will default to `null`. + + ListOfClassContainersProperty = new ClassListClassProperty( + "ListOfClassContainers" + , c => c.m_ListOfClassContainers + , c => new MyOtherClass() // create list item + ); + + ListOfStructContainersProperty = new StructListClassProperty( + "ListOfStructContainers" + , c => c.m_ListOfStructContainers + ); + } + + private static ClassPropertyBag s_PropertyBag { get; set; } + + private static void InitializePropertyBag() + { + // A property bag is simply an ordered list of properties + + s_PropertyBag = new ClassPropertyBag( + SimpleValueProperty + , ClassContainerProperty + , StructContainerProperty + , SimpleValueListProperty + , ListOfClassContainersProperty + , ListOfStructContainersProperty + ); + } + + static MyClass() + { + InitializeProperties(); + InitializePropertyBag(); + } + + // IPropertyContainer interface implementation + + public IPropertyBag PropertyBag => s_PropertyBag; + public IVersionStorage VersionStorage => null; +} + +class MyOtherClass : IPropertyContainer { } +struct MyOtherStruct : IStructPropertyContainer { } +``` + +## Struct Containers + +Property types available to `struct` containers all end with the `StructProperty` suffix. + +Struct containers require a bit more code to instrument, since they must be passed by reference in order to support data mutation during visits while preserving performance (no boxing, less copies). + +`struct` containers should therefore use the extended `IStructPropertyContainer` interface. + +```csharp +public struct MyStruct : IStructPropertyContainer +{ + public static ValueStructProperty NameProperty { get; private set; } + + private static StructPropertyBag s_PropertyBag { get; set; } + + public IPropertyBag PropertyBag => s_PropertyBag; + public IVersionStorage VersionStorage => null; + + private static void InitializeProperties() + { + // note that the delegates now get the container by reference + + NameProperty = new ValueStructProperty( + "Name" + ,(ref MyStruct c) => c.m_Name + ,(ref MyStruct c, string v) => c.m_Name = v + ); + } + + private static void InitializePropertyBag() + { + s_PropertyBag = new StructPropertyBag( + NameProperty + ); + } + + static MyStruct() + { + InitializeProperties(); + InitializePropertyBag(); + } + + private string m_Name; + + public string Name + { + get { return NameProperty.GetValue(ref this); } + set { NameProperty.SetValue(ref this, value); } + } + + // IStructPropertyContainer implementation + + public void MakeRef(ByRef byRef, TContext context) + { + byRef(ref this, context); + } + + public TReturn MakeRef(ByRef byRef, TContext context) + { + return byRef(ref this, context); + } +} +``` + +# Document revision history |Date|Reason| |---|---| -|Feb 14, 2018|Document created. Matches package version 0.1.0| \ No newline at end of file +|Nov 12, 2018|Removed Roslyn-base code generation path.| +|Oct 27, 2018|Added documentation around instrumentation and code generation.| +|Feb 14, 2018|Document created. Matches package version 0.1.0| diff --git a/package/Unity.Properties.Codegen/Unity.Properties.Codegen.asmdef b/package/Unity.Properties.Codegen/Unity.Properties.Codegen.asmdef index a33818c12..638d3de2f 100755 --- a/package/Unity.Properties.Codegen/Unity.Properties.Codegen.asmdef +++ b/package/Unity.Properties.Codegen/Unity.Properties.Codegen.asmdef @@ -1,6 +1,16 @@ { "name": "Unity.Properties.Codegen", - "references": ["Unity.Properties", "Unity.Properties.Codegen.Base", "Unity.Properties.Codegen.Analyzers"], - "includePlatforms": ["Editor"], - "excludePlatforms": [] -} + "references": [ + "Unity.Properties" + ], + "optionalUnityReferences": [], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [] +} \ No newline at end of file diff --git a/package/Unity.Properties/IProperty.cs b/package/Unity.Properties/IProperty.cs index 3b01c6956..631986ca0 100755 --- a/package/Unity.Properties/IProperty.cs +++ b/package/Unity.Properties/IProperty.cs @@ -5,17 +5,17 @@ namespace Unity.Properties { /// - /// Common nongeneric interface required for all property types + /// Interface required for all property types. /// public interface IProperty { /// - /// The name of this property + /// The name of this property. /// string Name { get; } /// - /// Container type that hosts the data for this property + /// Container type that hosts the data for this property. /// Type ContainerType { get; } } @@ -24,14 +24,31 @@ public interface IProperty * CLASS PROPERTIES */ + /// + /// Interface required for all properties where is a class. + /// public interface IClassProperty : IProperty { + /// + /// Performs a visit on the given container. + /// + /// Generic container to visit. + /// Visitor performing the visit. void Accept(IPropertyContainer container, IPropertyVisitor visitor); } + /// + /// Specialized for a given class container type. + /// + /// Property container type. public interface IClassProperty : IClassProperty where TContainer : class, IPropertyContainer { + /// + /// Performs a visit on the given container. + /// + /// Container to visit. + /// Visitor performing the visit. void Accept(TContainer container, IPropertyVisitor visitor); } @@ -39,14 +56,31 @@ public interface IClassProperty : IClassProperty * STRUCT PROPERTIES */ + /// + /// Interface required for all properties where is a struct. + /// public interface IStructProperty : IProperty { + /// + /// Performs a visit on the given container. + /// + /// Generic container reference to visit. + /// Visitor performing the visit. void Accept(ref IPropertyContainer container, IPropertyVisitor visitor); } + /// + /// Specialized for a given struct container type. + /// + /// Property container type. public interface IStructProperty : IStructProperty where TContainer : struct, IPropertyContainer { + /// + /// Performs a visit on the given container. + /// + /// Container reference to visit. + /// Visitor performing the visit. void Accept(ref TContainer container, IPropertyVisitor visitor); } } diff --git a/package/Unity.Properties/IPropertyBag.cs b/package/Unity.Properties/IPropertyBag.cs index 9ca1d6039..eecf73c0b 100755 --- a/package/Unity.Properties/IPropertyBag.cs +++ b/package/Unity.Properties/IPropertyBag.cs @@ -4,10 +4,26 @@ namespace Unity.Properties { + /// + /// Represents a collection of objects. + /// public interface IPropertyBag { + /// + /// Number of properties in the bag. + /// int PropertyCount { get; } + + /// + /// Enumeration of all available properties. + /// IEnumerable Properties { get; } + + /// + /// Finds a property by in this bag. + /// + /// Name of the property to look for. + /// The instance, or null if not found. IProperty FindProperty(string name); } } diff --git a/package/Unity.Properties/IPropertyContainer.cs b/package/Unity.Properties/IPropertyContainer.cs index 88df96733..38964e071 100755 --- a/package/Unity.Properties/IPropertyContainer.cs +++ b/package/Unity.Properties/IPropertyContainer.cs @@ -2,27 +2,74 @@ namespace Unity.Properties { + /// + /// Required interface for all types supported by algorithms. + /// public interface IPropertyContainer { + /// + /// object this container instance uses to keep track of property changes. + /// IVersionStorage VersionStorage { get; } + + /// + /// object defining the properties known to this container. + /// IPropertyBag PropertyBag { get; } } + /// + /// Intermediate function call allowing callers to get a struct container by reference. + /// + /// Container object reference. + /// Context object. + /// Type of the container object, must be a struct. + /// Type of the context object. public delegate void ByRef(ref TContainer container, TContext context) where TContainer : struct, IPropertyContainer; + /// + /// Intermediate function call allowing callers to get a struct container by reference. + /// + /// Container object reference. + /// Context object. + /// Type of the container object, must be a struct. + /// Type of the context object. + /// Type of the delegate's return value. public delegate TReturn ByRef(ref TContainer container, TContext context) where TContainer : struct, IPropertyContainer; + /// + /// Interface required on all struct types. + /// public interface IStructPropertyContainer : IPropertyContainer { } + /// + /// Specialized interface for a given struct container type. + /// + /// Container type, must be a struct. public interface IStructPropertyContainer : IStructPropertyContainer where TContainer : struct, IPropertyContainer { + /// + /// Calls the given method using this container's reference. + /// + /// Method to call. + /// Context object. + /// Context object type. void MakeRef(ByRef method, TContext context); + + /// + /// Calls the given method using this container's reference. + /// + /// Method to call. + /// Context object. + /// Context object type. + /// Return value type of the given method to call. + /// Return value of the given method. TReturn MakeRef(ByRef method, TContext context); } } diff --git a/package/Unity.Properties/IPropertyVisitor.cs b/package/Unity.Properties/IPropertyVisitor.cs index e6f6ac228..98acb5142 100755 --- a/package/Unity.Properties/IPropertyVisitor.cs +++ b/package/Unity.Properties/IPropertyVisitor.cs @@ -4,96 +4,216 @@ namespace Unity.Properties { - /// - /// Generic visitable class - /// - public interface IVisitableClass + internal interface IVisitableClass { void Visit(TContainer container, IPropertyVisitor visitor) where TContainer : class, IPropertyContainer; } - /// - /// Generic visitable struct class - /// - public interface IVisitableStruct + internal interface IVisitableStruct { void Visit(ref TContainer container, IPropertyVisitor visitor) where TContainer : struct, IPropertyContainer; } - /// - /// Typed visitable class - /// - /// - public interface IVisitableClass + internal interface IVisitableClass where TContainer : IPropertyContainer { void Visit(TContainer container, IPropertyVisitor visitor); } - /// - /// Typed visitable struct - /// - /// - public interface IVisitableStruct + internal interface IVisitableStruct where TContainer : struct, IPropertyContainer { void Visit(ref TContainer container, IPropertyVisitor visitor); } + /// + /// Represents a property visit context. + /// Object created by implementations and passed to the API. + /// + /// Type of the value being visited. public struct VisitContext { + /// + /// Property being visited. + /// public IProperty Property; + + /// + /// Property value. + /// public TValue Value; + + /// + /// Property value index within the collection. + /// If the is not a collection property, -1 is returned. + /// public int Index; } + /// + /// Base interface for all property visitors. + /// + /// + /// The intent of this interface is to support: non-allocating visits to class and struct , + /// visit exclusion and customization mechanisms, nested property containers, and collection properties. + /// public interface IPropertyVisitor { + /// + /// Whether or not to exclude the visit of this container in the given context. + /// + /// Container about to be visited. + /// Visit context object. + /// Container type. + /// Property value type. + /// true if the visit should be excluded, false otherwise. bool ExcludeVisit(TContainer container, VisitContext context) where TContainer : class, IPropertyContainer; + /// + /// Whether or not to exclude the visit of this container in the given context. + /// + /// Container about to be visited. + /// Visit context object. + /// Container type. + /// Property value type. + /// true if the visit should be excluded, false otherwise. bool ExcludeVisit(ref TContainer container, VisitContext context) where TContainer : struct, IPropertyContainer; + /// + /// Whether or not a custom visit was performed on this container in the given context. + /// + /// Container to visit. + /// Visit context object. + /// Container type. + /// Property value type. + /// true if a custom visit was performed, false otherwise. bool CustomVisit(TContainer container, VisitContext context) where TContainer : class, IPropertyContainer; + /// + /// Whether or not a custom visit was performed on this container in the given context. + /// + /// Container to visit. + /// Visit context object. + /// Container type. + /// Property value type. + /// true if a custom visit was performed, false otherwise. bool CustomVisit(ref TContainer container, VisitContext context) where TContainer : struct, IPropertyContainer; + /// + /// Performs a generic visit on this container in the given context. + /// + /// Container to visit. + /// Visit context object. + /// Container type. + /// Property value type. void Visit(TContainer container, VisitContext context) where TContainer : class, IPropertyContainer; + /// + /// Performs a generic visit on this container in the given context. + /// + /// Container to visit. + /// Visit context object. + /// Container type. + /// Property value type. void Visit(ref TContainer container, VisitContext context) where TContainer : struct, IPropertyContainer; + /// + /// Method called when visiting a nested property. + /// + /// Container to visit. + /// Visit context object. + /// Container type. + /// Property value type. + /// true if the visit should enter this nested container, false otherwise. bool BeginContainer(TContainer container, VisitContext context) where TContainer : class, IPropertyContainer where TValue : IPropertyContainer; + /// + /// Method called when visiting a nested property. + /// + /// Container to visit. + /// Visit context object. + /// Container type. + /// Property value type. + /// true if the visit should enter this nested container, false otherwise. bool BeginContainer(ref TContainer container, VisitContext context) where TContainer : struct, IPropertyContainer where TValue : IPropertyContainer; + /// + /// Method always called after , + /// regardless of its return value. + /// + /// Container to visit. + /// Visit context object. + /// Container type. + /// Property value type. void EndContainer(TContainer container, VisitContext context) where TContainer : class, IPropertyContainer where TValue : IPropertyContainer; + /// + /// Method always called after , + /// regardless of its return value. + /// + /// Container to visit. + /// Visit context object. + /// Container type. + /// Property value type. void EndContainer(ref TContainer container, VisitContext context) where TContainer : struct, IPropertyContainer where TValue : IPropertyContainer; + /// + /// Method called when visiting a collection property. + /// + /// Container to visit. + /// Visit context object. + /// Container type. + /// Property value type. + /// true if the visit should enter this collection, false otherwise. bool BeginCollection(TContainer container, VisitContext> context) where TContainer : class, IPropertyContainer; + /// + /// Method called when visiting a collection property. + /// + /// Container to visit. + /// Visit context object. + /// Container type. + /// Property value type. + /// true if the visit should enter this collection, false otherwise. bool BeginCollection(ref TContainer container, VisitContext> context) where TContainer : struct, IPropertyContainer; + /// + /// Method always called after , + /// regardless of its return value. + /// + /// Container to visit. + /// Visit context object. + /// Container type. + /// Property value type. void EndCollection(TContainer container, VisitContext> context) where TContainer : class, IPropertyContainer; + /// + /// Method always called after , + /// regardless of its return value. + /// + /// Container to visit. + /// Visit context object. + /// Container type. + /// Property value type. void EndCollection(ref TContainer container, VisitContext> context) where TContainer : struct, IPropertyContainer; } @@ -103,7 +223,7 @@ void EndCollection(ref TContainer container, VisitContext for usage. /// /// Property value type to visit. - public interface ICustomVisit + public interface ICustomVisit { /// /// Performs the visitor logic on this property value type. @@ -112,7 +232,7 @@ public interface ICustomVisit void CustomVisit(TValue value); } - /// + /// /// /// ICustomVisit declarations for all .NET primitive types. /// diff --git a/package/Unity.Properties/Migration/MigrationContainer.cs b/package/Unity.Properties/Migration/MigrationContainer.cs index 9e770c719..d11036a9c 100755 --- a/package/Unity.Properties/Migration/MigrationContainer.cs +++ b/package/Unity.Properties/Migration/MigrationContainer.cs @@ -669,7 +669,7 @@ public override void Accept(MigrationContainer container, IPropertyVisitor visit if (visitor.BeginContainer(container, context)) { - value.Visit(visitor); + value?.Visit(visitor); } visitor.EndContainer(container, context); } diff --git a/package/Unity.Properties/PropertyBag.cs b/package/Unity.Properties/PropertyBag.cs index c66076e2a..40319d592 100755 --- a/package/Unity.Properties/PropertyBag.cs +++ b/package/Unity.Properties/PropertyBag.cs @@ -90,8 +90,13 @@ public void Visit(TContainer container, IPropertyVisitor visitor) } else { - // Valid scenario when IPropertyContainer is used as TContainer + // Valid scenario when IPropertyContainer is used as TContainer for a class (property as IClassProperty)?.Accept(container, visitor); + + // Valid scenario when IPropertyContainer is used as TContainer for a struct + // @TODO Fixme + IPropertyContainer c = container; + (property as IStructProperty)?.Accept(ref c, visitor); } } } diff --git a/package/Unity.Properties/PropertyPath.cs b/package/Unity.Properties/PropertyPath.cs index b49306210..4c0ba6871 100755 --- a/package/Unity.Properties/PropertyPath.cs +++ b/package/Unity.Properties/PropertyPath.cs @@ -47,6 +47,21 @@ public PropertyPath(PropertyPath path) m_Parts = new List(path.m_Parts); } + public void Insert(int index, string propertyName, int listIndex = InvalidListIndex) + { + Assert.IsFalse(string.IsNullOrEmpty(propertyName)); + + if (listIndex < 0) + { + listIndex = InvalidListIndex; + } + m_Parts.Insert(index, new Part() + { + propertyName = propertyName, + listIndex = listIndex + }); + } + public void Push(string propertyName, int listIndex = InvalidListIndex) { Assert.IsFalse(string.IsNullOrEmpty(propertyName)); diff --git a/package/Unity.Properties/Value/Impl/ValuePropertyImpl.cs b/package/Unity.Properties/Value/Impl/ValuePropertyImpl.cs index 4ff0d098f..805950254 100755 --- a/package/Unity.Properties/Value/Impl/ValuePropertyImpl.cs +++ b/package/Unity.Properties/Value/Impl/ValuePropertyImpl.cs @@ -82,7 +82,7 @@ public ValueClassProperty(string name, GetValueMethod getValue, SetValueMethod s public override void Accept(TContainer container, IPropertyVisitor visitor) { var context = new VisitContext {Property = this, Value = GetValue(container), Index = -1}; - if (!visitor.ExcludeOrCustomVisit(container, context)) + if (false == visitor.ExcludeOrCustomVisit(container, context)) { visitor.Visit(container, context); } @@ -91,7 +91,7 @@ public override void Accept(TContainer container, IPropertyVisitor visitor) /// /// - /// Property of a class thats hosts a nested class (IPropertyContainer) value + /// Property of a class that hosts a nested class (IPropertyContainer) value /// /// /// diff --git a/package/package.json b/package/package.json index 2235e858b..6e3991a8d 100755 --- a/package/package.json +++ b/package/package.json @@ -1,10 +1,10 @@ { "name": "com.unity.properties", "displayName":"Property API", - "version": "0.3.8-preview", + "version": "0.4.0-preview", "unity": "2018.1", "description": "Interfaces and utilities to describe and visit data containers.", - "keywords": ["properties", "property", "schema", "api"], + "keywords": ["properties", "property"], "dependencies": { } } diff --git a/versions.txt b/versions.txt index 1a5c6693e..997f52fd9 100755 --- a/versions.txt +++ b/versions.txt @@ -43,3 +43,4 @@ 0.3.6-preview 0.3.7-preview 0.3.8-preview +0.4.0-preview