public fiDeserializedObject(fiSerializedObject serializedState)
        {
            Type targetType = serializedState.Target.Target.GetType();

            var serializationOperator = new fiSerializationOperator()
            {
                SerializedObjects = serializedState.ObjectReferences
            };

            // Fetch the serializer that the target uses
            Type serializerType = BehaviorTypeToSerializerTypeMap.GetSerializerType(targetType);
            var  serializer     = (BaseSerializer)fiSingletons.Get(serializerType);

            var inspectedType = InspectedType.Get(targetType);

            Members = new List <fiDeserializedMember>();

            foreach (fiSerializedMember member in serializedState.Members)
            {
                InspectedProperty property = inspectedType.GetPropertyByName(member.Name);
                if (property != null)
                {
                    object deserialized = serializer.Deserialize(
                        fsPortableReflection.AsMemberInfo(property.StorageType), member.Value, serializationOperator);
                    Members.Add(new fiDeserializedMember()
                    {
                        InspectedProperty = property,
                        Value             = deserialized,
                        ShouldRestore     = member.ShouldRestore
                    });
                }
            }
        }
Beispiel #2
0
 /// <summary>
 /// The data path accesses an element inside of an IDictionary instance.
 /// The dictionary instance resides at |property|.
 /// </summary>
 public ObjectDataPath(InspectedProperty property, object dictKey)
 {
     byProperty  = property;
     byListIndex = -1;
     byDictKey   = dictKey;
     byType      = null;
 }
Beispiel #3
0
 /// <summary>
 /// The data path accesses an element inside of an IList instance
 /// (including arrays). The list instance resides at |property|.
 /// </summary>
 public ObjectDataPath(InspectedProperty property, int listIndex)
 {
     byProperty  = property;
     byListIndex = listIndex;
     byDictKey   = null;
     byType      = null;
 }
        private static bool SaveStateForProperty(ISerializedObject obj, InspectedProperty property, BaseSerializer serializer, ISerializationOperator serializationOperator, out string serializedValue, ref bool success)
        {
            object currentValue = property.Read(obj);

            try {
                if (currentValue == null)
                {
                    serializedValue = null;
                }
                else
                {
                    serializedValue = serializer.Serialize(property.MemberInfo, currentValue, serializationOperator);
                }
                return(true);
            }
            catch (Exception e) {
                success         = false;
                serializedValue = null;

                Debug.LogError("Exception caught when serializing property <" +
                               property.Name + "> in <" + obj + "> with value " + currentValue + "\n" +
                               e);
                return(false);
            }
        }
Beispiel #5
0
        /// <summary>
        /// A helper method that draws the inspector for a field/property at the given location.
        /// </summary>
        private void EditProperty(ref Rect region, object element, InspectedProperty property, fiGraphMetadata metadata)
        {
            bool hasPrefabDiff = fiPrefabTools.HasPrefabDiff(element, property);

            if (hasPrefabDiff)
            {
                fiUnityInternalReflection.SetBoldDefaultFont(true);
            }

            // edit the property
            {
                var childMetadata = metadata.Enter(property.Name);
                fiGraphMetadataCallbacks.PropertyMetadataCallback(childMetadata.Metadata, property);

                Rect  propertyRect   = region;
                float propertyHeight = fiEditorGUI.EditPropertyHeight(element, property, childMetadata);
                propertyRect.height = propertyHeight;

                fiEditorGUI.EditProperty(propertyRect, element, property, childMetadata);

                region.y += propertyHeight;
            }

            if (hasPrefabDiff)
            {
                fiUnityInternalReflection.SetBoldDefaultFont(false);
            }
        }
        /// <summary>
        /// Returns true if the given property should be displayed in the inspector. This method
        /// assumes that the property type is inspectable.
        /// </summary>
        private static bool ShouldDisplayProperty(InspectedProperty property) {
            var memberInfo = property.MemberInfo;

            // note: we do opt-out before opt-in so that we can still serialize
            //       a field but not display it in the inspector (as the serialize
            //       annotations automatically cause a field to be displayed)
            if (memberInfo.IsDefined(typeof(HideInInspector), /*inherit:*/ true) ||
                memberInfo.IsDefined(typeof(NotSerializedAttribute), /*inherit:*/true) ||
                fiInstalledSerializerManager.SerializationOptOutAnnotations.Any(t => memberInfo.IsDefined(t, /*inherit*/true))) {

                return false;
            }

            if (memberInfo.IsDefined(typeof(ShowInInspectorAttribute), /*inherit:*/ true) ||
                (property.IsStatic == false && fiInstalledSerializerManager.SerializationOptInAnnotations.Any(t => memberInfo.IsDefined(t, /*inherit*/true)))) {

                return true;
            }

            if (property.MemberInfo is PropertyInfo && fiSettings.InspectorRequireShowInInspector) {
                return false;
            }

            return
                InspectedType.IsSerializedByFullInspector(property) ||
                InspectedType.IsSerializedByUnity(property);
        }
        /// <summary>
        /// Returns true if the given property should be serialized.
        /// </summary>
        /// <remarks>This assumes that the property is r/w!</remarks>
        public static bool IsSerializedByFullInspector(InspectedProperty property)
        {
            if (property.IsStatic) {
                return false;
            }

            // Do not bother serializing BaseObject derived types - they will handle it properly
            // themselves. Serializing them will only lead to wasted storage space.
            if (typeof(BaseObject).Resolve().IsAssignableFrom(property.StorageType.Resolve())) {
                return false;
            }

            MemberInfo member = property.MemberInfo;

            // if it has NonSerialized, then we *don't* serialize it
            if (fsPortableReflection.HasAttribute<NonSerializedAttribute>(member) ||
                fsPortableReflection.HasAttribute<NotSerializedAttribute>(member)) {
                return false;
            }

            // Don't serialize it if it has one of the custom opt-out annotations either
            var optOut = fiInstalledSerializerManager.SerializationOptOutAnnotations;
            for (int i = 0; i < optOut.Length; ++i) {
                if (member.IsDefined(optOut[i], true)) {
                    return false;
                }
            }

            // if we have a [SerializeField] or [Serializable] attribute, then we *do* serialize
            if (fsPortableReflection.HasAttribute<SerializeField>(member) ||
                fsPortableReflection.HasAttribute<SerializableAttribute>(member)) {
                return true;
            }

            // Serialize if we have a custom opt-in annotation
            var optIn = fiInstalledSerializerManager.SerializationOptInAnnotations;
            for (int i = 0; i < optIn.Length; ++i) {
                if (member.IsDefined(optIn[i], /*inherit:*/ true)) {
                    return true;
                }
            }

            if (property.MemberInfo is PropertyInfo) {
                // perf: property.IsAutoProperty is lazily computed, so if we have SerializeAutoProperties set
                //       to false we can avoid computing any auto-property checks by putting the
                //       SerializeAutoProperties check before the IsAutoProperty check

                // If we're not serializing auto-properties, then we will not serialize any properties by default
                if (fiSettings.SerializeAutoProperties == false) return false;

                // If it's not an auto property, then we are not going to serialize it by default
                if (property.IsAutoProperty == false) return false;
            }

            return property.IsPublic;
        }
        /// <summary>
        ///     Initializes a new instance of the <see cref="KeyPropertyKeyBinding" /> class.
        /// </summary>
        /// <param name="inspectedProperty">
        ///     The inspected property.
        /// </param>
        /// <param name="columnBinding">
        ///     The column binding.
        /// </param>
        /// <exception cref="ArgumentException">
        ///     If the <paramref name="inspectedProperty" /> does not have an entity key.
        /// </exception>
        internal KeyPropertyKeyBinding(InspectedProperty inspectedProperty, IColumnBinding columnBinding)
            : base(inspectedProperty, columnBinding)
        {
            if (!inspectedProperty.HasKey)
            {
                throw new ArgumentException("Invalid property, missing entity key", nameof(inspectedProperty));
            }

            this.get = inspectedProperty.Getter;
            this.set = inspectedProperty.Setter;
        }
        /// <summary>
        /// Helper function that just ignores a few FI internal types for serialization since the
        /// backup solution serializes all inspected properties, not just those that are serialized
        /// </summary>
        private static bool ShouldIgnoreForPersist(InspectedProperty property)
        {
            string name = property.Name;

            return
                (name.Contains("ISerializedObject.") ||
                 name == "_objectReferences" ||
                 name == "_serializedStateKeys" ||
                 name == "_serializedStateValues" ||
                 name == "_restored");
        }
Beispiel #10
0
        public static float EditPropertyHeightDirect(InspectedProperty property, object propertyValue, fiGraphMetadataChild metadataChild)
        {
            fiGraphMetadata metadata = metadataChild.Metadata;

            var editor = PropertyEditor.Get(property.StorageType, property.MemberInfo).FirstEditor;

            GUIContent propertyLabel = new GUIContent(property.DisplayName);

            // Either the foldout is active or we are not displaying a foldout.
            // Either way, we want to report the full height of the property.
            return(editor.GetElementHeight(propertyLabel, propertyValue, metadata.Enter("EditProperty", metadata.Context)));
        }
        /// <summary>
        /// Returns true if the property type itself is inspectable. This does
        /// not necessarily mean that the property should be displayed in the
        /// inspector -- just that the FI editing engine can handle it.
        /// </summary>
        private static bool IsPropertyTypeInspectable(InspectedProperty property)
        {
            // We never inspect delegates
            if (typeof(Delegate).IsAssignableFrom(property.StorageType)) {
                return false;
            }

            if (property.MemberInfo is FieldInfo) {
                // Don't inspect compiler generated fields (an example would be a
                // backing field for an automatically generated property).
                if (property.MemberInfo.IsDefined(typeof(CompilerGeneratedAttribute), /*inherit:*/ false)) {
                    return false;
                }
            }
            else if (property.MemberInfo is PropertyInfo) {
                var propertyInfo = (PropertyInfo)property.MemberInfo;

                // If we cannot read from the property, then there is no sense in
                // displaying it -- we will have no value to display
                if (propertyInfo.CanRead == false) {
                    return false;
                }

                // hack?: We only display r/w properties declared on Unity types
                // note: This may rely on the fact that we collect members
                //       locally per inheritance level (does DeclaringType
                //       change? I'm not sure).
                // note: We also check for UnityEditor since some users use FI in
                //       non-standard ways -- ie, potentially for types that are
                //       not available at runtime and hence may be in the
                //       UnityEditor namespace.
                var @namespace = propertyInfo.DeclaringType.Namespace;
                if (@namespace != null &&
                    (@namespace.StartsWith("UnityEngine") || @namespace.StartsWith("UnityEditor"))) {
                    if (propertyInfo.CanWrite == false) {
                        return false;
                    }
                }

                // If the property is named "Item", it might be the this[int]
                // indexer, which in that case we don't serialize it We cannot
                // just compare with "Item" because of explicit interfaces, where
                // the name of the property will be the full method name.
                if (propertyInfo.Name.EndsWith("Item")) {
                    ParameterInfo[] parameters = propertyInfo.GetIndexParameters();
                    if (parameters.Length > 0) {
                        return false;
                    }
                }
            }

            return true;
        }
Beispiel #12
0
        /// <summary>
        ///     Writes the specified property.
        /// </summary>
        /// <param name="serializer">
        ///     The serializer.
        /// </param>
        /// <param name="inspectedProperty">
        ///     The inspected property.
        /// </param>
        /// <param name="any">
        ///     The object to which the property belongs.
        /// </param>
        private static void WriteProperty(Serializer serializer, InspectedProperty inspectedProperty, object any)
        {
            var value = inspectedProperty.Getter(any);

            if (value == null)
            {
                Encoder.WriteTag(serializer.BinaryWriter, Tags.Null);
            }
            else
            {
                serializer.Write(inspectedProperty.PropertyType, value.GetType(), value);
            }
        }
Beispiel #13
0
        /// <summary>
        ///     Encodes the specified property.
        /// </summary>
        /// <param name="serializer">
        ///     The serializer.
        /// </param>
        /// <param name="inspectedProperty">
        ///     The inspected property.
        /// </param>
        /// <param name="encoderInfo">
        ///     The encoder info.
        /// </param>
        /// <param name="any">
        ///     The object to which the property belongs.
        /// </param>
        private static void EncodeProperty(Serializer serializer, InspectedProperty inspectedProperty,
                                           EncoderInfo encoderInfo, object any)
        {
            var value = inspectedProperty.Getter(any);

            if (value == null)
            {
                Encoder.WriteTag(serializer.BinaryWriter, Tags.Null);
            }
            else
            {
                serializer.Encode(encoderInfo, value, true);
            }
        }
Beispiel #14
0
        public static void EditProperty(Rect region, object container, InspectedProperty property, fiGraphMetadataChild metadata)
        {
            EditorGUI.BeginChangeCheck();

            object propertyValue = property.Read(container);
            object updatedValue  = EditPropertyDirect(region, property, propertyValue, metadata, container);

            if (EditorGUI.EndChangeCheck())
            {
                property.Write(container, updatedValue);

                // Make sure we propagate the changes up the edit stack. For example, if this property
                // is on a struct on a struct, then the top-level struct will not get modified without
                // propagation of the change check.
                GUI.changed = true;
            }
        }
Beispiel #15
0
        public bool Read(object obj, out object result)
        {
            if (byType != null)
            {
                result = obj;
                return(true);
            }

            // The property was potentially found on a different type. We need to
            // update it to associate with this type. It's very possible the
            // property will not even apply to context, in which case Write
            // becomes a no-op.
            InspectedProperty propertyToUse = byProperty;

            if (byProperty.MemberInfo.DeclaringType != obj.GetType())
            {
                var childProp = InspectedType.Get(obj.GetType()).GetPropertyByName(byProperty.Name);
                if (childProp != null)
                {
                    propertyToUse = childProp;
                }
                else
                {
                    result = null;
                    return(false);
                }
            }

            var read = propertyToUse.Read(obj);

            if (byListIndex >= 0)
            {
                result = ((IList)read)[byListIndex];
                return(true);
            }
            if (byDictKey != null)
            {
                result = ((IDictionary)read)[byDictKey];
                return(true);
            }

            result = read;
            return(true);
        }
Beispiel #16
0
        /// <summary>
        ///     Gets the key binding for the inspected property specified.
        /// </summary>
        /// <param name="property">
        ///     The inspected property.
        /// </param>
        /// <param name="columnBinding">
        ///     The column binding.
        /// </param>
        /// <returns>
        ///     The key binding.
        /// </returns>
        private static IKeyBinding GetKeyBindingForProperty(InspectedProperty property, IColumnBinding columnBinding)
        {
            if (property.PropertyType == typeof(Key))
            {
                return(new KeyPropertyKeyBinding(property, columnBinding));
            }

            if (property.PropertyType == typeof(string))
            {
                return(new StringPropertyKeyBinding(property, columnBinding));
            }

            if (property.PropertyType == typeof(Guid))
            {
                return(new GuidPropertyKeyBinding(property, columnBinding));
            }

            throw new PersistenceException(String.Format(CultureInfo.InvariantCulture,
                                                         @"Unsupported id property type {0}", property.PropertyType));
        }
Beispiel #17
0
        private static void RevertPrefabContextMenu(Rect region, object context, InspectedProperty property)
        {
            if (Event.current.type == EventType.ContextClick &&
                region.Contains(Event.current.mousePosition) &&

                // This can be a relatively heavy function call, so we check it last. If the rect bounds
                // check ends up consuming lots of time, then HasPrefabDiff has a small fast-path section
                // that can short-circuit the bounds check.
                fiPrefabTools.HasPrefabDiff(context, property))
            {
                Event.current.Use();

                var content = new GUIContent("Revert " + property.DisplayName + " to Prefab Value");

                GenericMenu menu = new GenericMenu();
                menu.AddItem(content, /*on:*/ false, () => {
                    fiPrefabTools.RevertValue(context, property);
                });
                menu.ShowAsContext();
            }
        }
        /// <summary>
        /// Restores a backup that was previously created.
        /// </summary>
        public static void RestoreBackup(fiSerializedObject serializedState)
        {
            Type targetType    = serializedState.Target.Target.GetType();
            var  inspectedType = InspectedType.Get(targetType);

            var serializationOperator = new fiSerializationOperator()
            {
                SerializedObjects = serializedState.ObjectReferences
            };

            // Fetch the serializer that the target uses
            Type serializerType = BehaviorTypeToSerializerTypeMap.GetSerializerType(targetType);
            var  serializer     = (BaseSerializer)fiSingletons.Get(serializerType);

            foreach (fiSerializedMember member in serializedState.Members)
            {
                // user requested a skip for restoring this property
                if (member.ShouldRestore.Enabled == false)
                {
                    continue;
                }

                InspectedProperty property = inspectedType.GetPropertyByName(member.Name);
                if (property != null)
                {
                    Type   storageType   = property.StorageType;
                    object restoredValue = serializer.Deserialize(storageType, member.Value, serializationOperator);
                    property.Write(serializedState.Target.Target, restoredValue);
                }
            }

            if (serializedState.Target.Target is ISerializedObject)
            {
                var serializedObj = ((ISerializedObject)serializedState.Target.Target);
                serializedObj.SaveState();
                serializedObj.RestoreState();
            }
        }
Beispiel #19
0
        /// <summary>
        /// Draws a GUI for editing the given property and returns the updated
        /// value. This does
        /// *not* write the updated value to a container.
        /// </summary>
        /// <param name="context">
        /// An optional context that the property value came from. If this is not
        /// given, then a prefab context menu will not be displayable.
        /// </param>
        public static object EditPropertyDirect(Rect region, InspectedProperty property, object propertyValue, fiGraphMetadataChild metadataChild, object context)
        {
            fiGraphMetadata metadata = metadataChild.Metadata;

            // Show a "revert to prefab" value context-menu if possible
            if (context != null)
            {
                RevertPrefabContextMenu(region, context, property);
            }

            // get the label / tooltip
            GUIContent label = new GUIContent(property.DisplayName,
                                              InspectorTooltipAttribute.GetTooltip(property.MemberInfo));

            var             editorChain = PropertyEditor.Get(property.StorageType, property.MemberInfo);
            IPropertyEditor editor      = editorChain.FirstEditor;

            EditorGUI.BeginDisabledGroup(property.CanWrite == false);
            propertyValue = editor.Edit(region, label, propertyValue, metadata.Enter("EditProperty", metadata.Context));
            EditorGUI.EndDisabledGroup();

            return(propertyValue);
        }
Beispiel #20
0
        /// <summary>
        ///     Sets an object property.
        /// </summary>
        /// <param name="inspectedProperty">
        ///     The inspected property.
        /// </param>
        /// <param name="target">
        ///     The target.
        /// </param>
        /// <param name="value">
        ///     The property value.
        /// </param>
        /// <param name="tag">
        ///     The tag.
        /// </param>
        internal override void SetObjectProperty(InspectedProperty inspectedProperty, object target, object value,
                                                 Tags tag)
        {
            var entitySpec = value as EntitySpec;

            if (entitySpec != null)
            {
                var entityScanTarget = new EntityScanTarget(inspectedProperty, entitySpec, target);
                if (IsEntityReference(tag))
                {
                    this.entityScanner.Add(entityScanTarget);
                    this.ObjectRefs.Add(entityScanTarget);
                }
                else if (tag == Tags.ObjectRef)
                {
                    ((EntityScanTarget)entitySpec).AddScanTargetRef(entityScanTarget);
                }
            }
            else
            {
                base.SetObjectProperty(inspectedProperty, target, value, tag);
            }
        }
        private static bool TryToCopyValues(ISerializedObject newInstance)
        {
            if (string.IsNullOrEmpty(newInstance.SharedStateGuid))
            {
                return(false);
            }

            ISerializedObject originalInstance = null;

            lock (_skipSerializationQueue) {
                if (!_skipSerializationQueue.TryGetValue(newInstance.SharedStateGuid, out originalInstance))
                {
                    return(false);
                }
                _skipSerializationQueue.Remove(newInstance.SharedStateGuid);
            }

            // After a prefab is instantiated Unity will call a full
            // serialize/deserialize cycle on the object. We don't need to copy
            // values if the object references are the same.
            if (ReferenceEquals(newInstance, originalInstance))
            {
                return(true);
            }

            var inspectedType = InspectedType.Get(originalInstance.GetType());

            for (int i = 0; i < originalInstance.SerializedStateKeys.Count; ++i)
            {
                InspectedProperty property =
                    inspectedType.GetPropertyByName(originalInstance.SerializedStateKeys[i]) ??
                    inspectedType.GetPropertyByFormerlySerializedName(originalInstance.SerializedStateKeys[i]);
                property.Write(newInstance, property.Read(originalInstance));
            }
            return(true);
        }
Beispiel #22
0
        /// <summary>
        /// Serializes the current state of the given object.
        /// </summary>
        /// <typeparam name="TSerializer">The type of serializer to use for the serialization
        /// process.</typeparam>
        /// <param name="obj">The object that should be serialized.</param>
        /// <returns>True if serialization was entirely successful, false if something bad happened along the way.</returns>
        public static bool SaveState <TSerializer>(ISerializedObject obj)
            where TSerializer : BaseSerializer
        {
            bool success = true;

            // short-circuit for null serializer
            if (typeof(TSerializer) == typeof(NullSerializer))
            {
                return(success);
            }

            var callbacks = obj as ISerializationCallbacks;

            if (callbacks != null)
            {
                callbacks.OnBeforeSerialize();
            }

            // fetch the selected serializer
            var serializer = fiSingletons.Get <TSerializer>();

            // setup the serialization operator
            var serializationOperator = fiSingletons.Get <ListSerializationOperator>();

            serializationOperator.SerializedObjects = new List <UnityObject>();

            // get the properties that we will be serializing
            var properties = InspectedType.Get(obj.GetType()).GetProperties(InspectedMemberFilters.FullInspectorSerializedProperties);

            var serializedKeys   = new List <string>();
            var serializedValues = new List <string>();

            for (int i = 0; i < properties.Count; ++i)
            {
                InspectedProperty property     = properties[i];
                object            currentValue = property.Read(obj);

                try {
                    if (currentValue == null)
                    {
                        serializedKeys.Add(property.Name);
                        serializedValues.Add(null);
                    }
                    else
                    {
                        var serializedState = serializer.Serialize(property.MemberInfo, currentValue, serializationOperator);
                        serializedKeys.Add(property.Name);
                        serializedValues.Add(serializedState);
                    }
                }
                catch (Exception e) {
                    success = false;
                    Debug.LogError("Exception caught when serializing property <" +
                                   property.Name + "> in <" + obj + "> with value " + currentValue + "\n" +
                                   e);
                }
            }

            // Write the updated data out to the object.

            // Note that we only write data out to the object if our serialized state has
            // changed. Unity will blindly rewrite the data on disk which will cause some
            // source control systems to check-out the files. If we are just updating
            // the content to the same content, we do not want to cause an accidental
            // checkout.

            if (AreListsDifferent(obj.SerializedStateKeys, serializedKeys))
            {
                obj.SerializedStateKeys = serializedKeys;
            }
            if (AreListsDifferent(obj.SerializedStateValues, serializedValues))
            {
                obj.SerializedStateValues = serializedValues;
            }
            if (AreListsDifferent(obj.SerializedObjectReferences, serializationOperator.SerializedObjects))
            {
                obj.SerializedObjectReferences = serializationOperator.SerializedObjects;
            }

            // Calling SetDirty seems cause prefab instances to have prefab differences after a script
            // reload, so we only call SetDirty on ScriptableObjects (it's only really necessary for those as well)
            if (obj is ScriptableObject)
            {
                fiLateBindings.EditorUtility.SetDirty((ScriptableObject)obj);
            }

            if (callbacks != null)
            {
                callbacks.OnAfterSerialize();
            }

            fiEditorSerializationManager.MarkSerialized(obj);

            return(success);
        }
        /// <summary>
        /// Returns true if the given type can be serialized by Unity. This
        /// function is conservative and may not return true if the type can be
        /// serialized by unity. However, it will *not* return true if the type
        /// cannot be serialized by unity.
        /// </summary>
        public static bool IsSerializedByUnity(InspectedProperty property)
        {
            // Properties are *not* serialized by Unity
            if (property.MemberInfo is PropertyInfo) {
                return false;
            }

            // statics are *not* serialized by Unity
            if (property.IsStatic) {
                return false;
            }

            // readonly fields are *not* serialized by Unity
            FieldInfo fieldInfo = property.MemberInfo as FieldInfo;
            if (fieldInfo.IsInitOnly) {
                return false;
            }

            // If the attribute is not public and doesn't have a [SerializeField]
            // attribute, then Unity will not serialize it, regardless of type.
            if (property.IsPublic == false &&
                property.MemberInfo.IsDefined(typeof(SerializeField), /*inherit:*/ true) == false) {
                return false;
            }

            Type type = property.StorageType;

            return
                // Basic primitive types
                IsSimpleTypeThatUnityCanSerialize(type) ||

                // A non-generic UnityObject derived type
                (typeof(UnityObject).IsAssignableFrom(type) && type.Resolve().IsGenericType == false) ||

                // Array (but not a multidimensional one)
                (type.IsArray && type.GetElementType().IsArray == false && IsSimpleTypeThatUnityCanSerialize(type.GetElementType())) ||

                // Lists of already serializable types
                (
                    type.Resolve().IsGenericType &&
                    type.GetGenericTypeDefinition() == typeof(List<>) &&
                    IsSimpleTypeThatUnityCanSerialize(type.GetGenericArguments()[0])
                );
        }
        /// <summary>
        /// Serializes the current state of the given object.
        /// </summary>
        /// <typeparam name="TSerializer">
        /// The type of serializer to use for the serialization process.
        /// </typeparam>
        /// <param name="obj">The object that should be serialized.</param>
        /// <returns>
        /// True if serialization was entirely successful, false if something bad
        /// happened along the way.
        /// </returns>
        public static bool SaveState <TSerializer>(ISerializedObject obj)
            where TSerializer : BaseSerializer
        {
            fiLog.Log(typeof(fiISerializedObjectUtility), "Serializing object of type {0}", obj.GetType());

            bool success = true;

            var callbacks = obj as ISerializationCallbacks;

            if (callbacks != null)
            {
                callbacks.OnBeforeSerialize();
            }

            // Skip serialization entirely if requested.
            if (!string.IsNullOrEmpty(obj.SharedStateGuid))
            {
                SkipCloningValues(obj);
                if (callbacks != null)
                {
                    callbacks.OnAfterSerialize();
                }
                return(true);
            }

            // fetch the selected serializer
            var serializer = fiSingletons.Get <TSerializer>();

            // setup the serialization operator
            var serializationOperator = fiSingletons.Get <ListSerializationOperator>();

            serializationOperator.SerializedObjects = new List <UnityObject>();

            // get the properties that we will be serializing
            var serializedKeys   = new List <string>();
            var serializedValues = new List <string>();

            // PERF: Calling InspectedType().GetProperties() is extremely
            //       expensive. If we're not in the editor, then we can avoid
            //       making that call because the only time we serialize an
            //       object is if it has existing FS data. If it has FS data,
            //       then that data stores the set of properties which are
            //       serialized, so we can just hijack that information when
            //       doing the serialization to determine which properties to
            //       serialize.

            if (fiUtility.IsEditor || obj.SerializedStateKeys == null || obj.SerializedStateKeys.Count == 0)
            {
                var properties = InspectedType.Get(obj.GetType()).GetProperties(InspectedMemberFilters.FullInspectorSerializedProperties);
                for (int i = 0; i < properties.Count; ++i)
                {
                    InspectedProperty property = properties[i];
                    string            serializedValue;
                    if (SaveStateForProperty(obj, property, serializer, serializationOperator, out serializedValue, ref success))
                    {
                        serializedKeys.Add(property.Name);
                        serializedValues.Add(serializedValue);
                    }
                }
            }
            else
            {
                var inspectedType = InspectedType.Get(obj.GetType());
                for (int i = 0; i < obj.SerializedStateKeys.Count; ++i)
                {
                    InspectedProperty property = inspectedType.GetPropertyByName(obj.SerializedStateKeys[i]) ?? inspectedType.GetPropertyByFormerlySerializedName(obj.SerializedStateKeys[i]);
                    if (property == null)
                    {
                        continue;
                    }

                    string serializedValue;
                    if (SaveStateForProperty(obj, property, serializer, serializationOperator, out serializedValue, ref success))
                    {
                        serializedKeys.Add(property.Name);
                        serializedValues.Add(serializedValue);
                    }
                }
            }

            // Write the updated data out to the object.

            // Note that we only write data out to the object if our serialized
            // state has changed. Unity will blindly rewrite the data on disk
            // which will cause some source control systems to check-out the
            // files. If we are just updating the content to the same content, we
            // do not want to cause an accidental checkout.

            bool changed = false;

            if (AreListsDifferent(obj.SerializedStateKeys, serializedKeys))
            {
                obj.SerializedStateKeys = serializedKeys;
                changed = true;
            }
            if (AreListsDifferent(obj.SerializedStateValues, serializedValues))
            {
                obj.SerializedStateValues = serializedValues;
                changed = true;
            }
            if (AreListsDifferent(obj.SerializedObjectReferences, serializationOperator.SerializedObjects))
            {
                obj.SerializedObjectReferences = serializationOperator.SerializedObjects;
                changed = true;
            }

            if (changed && fiUtility.IsEditor)
            {
                fiLateBindings.EditorApplication.InvokeOnEditorThread(() => {
                    var unityObj = (UnityObject)obj;
                    if (unityObj != null)
                    {
                        fiLateBindings.EditorUtility.SetDirty(unityObj);
                    }
                });
            }

            if (callbacks != null)
            {
                callbacks.OnAfterSerialize();
            }

            return(success);
        }
Beispiel #25
0
 /// <summary>
 ///     Initializes a new instance of the <see cref="GuidPropertyKeyBinding" /> class.
 /// </summary>
 /// <param name="inspectedProperty">
 ///     The inspected property.
 /// </param>
 /// <param name="columnBinding">
 ///     The column binding.
 /// </param>
 internal GuidPropertyKeyBinding(InspectedProperty inspectedProperty, IColumnBinding columnBinding)
     : base(inspectedProperty, columnBinding)
 {
     this.get = inspectedProperty.Getter;
     this.set = inspectedProperty.Setter;
 }
 public bool IsInterested(InspectedProperty property) {
     return
         property.CanWrite &&
         InspectedType.IsSerializedByFullInspector(property) &&
         InspectedType.IsSerializedByUnity(property) == false;
 }
        /// <summary>
        /// Serializes the current state of the given object.
        /// </summary>
        /// <typeparam name="TSerializer">The type of serializer to use for the serialization
        /// process.</typeparam>
        /// <param name="obj">The object that should be serialized.</param>
        /// <returns>True if serialization was entirely successful, false if something bad happened along the way.</returns>
        public static bool SaveState <TSerializer>(ISerializedObject obj)
            where TSerializer : BaseSerializer
        {
            bool success = true;

            var callbacks = obj as ISerializationCallbacks;

            if (callbacks != null)
            {
                callbacks.OnBeforeSerialize();
            }

            // fetch the selected serializer
            var serializer = fiSingletons.Get <TSerializer>();

            // setup the serialization operator
            var serializationOperator = fiSingletons.Get <ListSerializationOperator>();

            serializationOperator.SerializedObjects = new List <UnityObject>();

            // get the properties that we will be serializing
            var serializedKeys   = new List <string>();
            var serializedValues = new List <string>();

            // PERF: Calling InspectedType().GetProperties() is extremely expensive. If we're not in the editor, then we can avoid making
            //       that call because the only time we serialize an object is if it has existing FS data. If it has FS data, then that data
            //       stores the set of properties which are serialized, so we can just hijack that information when doing the serialization to
            //       determine which properties to serialize.

            if (fiUtility.IsEditor || obj.SerializedStateKeys == null || obj.SerializedStateKeys.Count == 0)
            {
                var properties = InspectedType.Get(obj.GetType()).GetProperties(InspectedMemberFilters.FullInspectorSerializedProperties);
                for (int i = 0; i < properties.Count; ++i)
                {
                    InspectedProperty property = properties[i];
                    string            serializedValue;
                    if (SaveStateForProperty(obj, property, serializer, serializationOperator, out serializedValue, ref success))
                    {
                        serializedKeys.Add(property.Name);
                        serializedValues.Add(serializedValue);
                    }
                }
            }
            else
            {
                var inspectedType = InspectedType.Get(obj.GetType());
                for (int i = 0; i < obj.SerializedStateKeys.Count; ++i)
                {
                    InspectedProperty property = inspectedType.GetPropertyByName(obj.SerializedStateKeys[i]) ?? inspectedType.GetPropertyByFormerlySerializedName(obj.SerializedStateKeys[i]);
                    if (property == null)
                    {
                        continue;
                    }

                    string serializedValue;
                    if (SaveStateForProperty(obj, property, serializer, serializationOperator, out serializedValue, ref success))
                    {
                        serializedKeys.Add(property.Name);
                        serializedValues.Add(serializedValue);
                    }
                }
            }

            // Write the updated data out to the object.

            // Note that we only write data out to the object if our serialized state has
            // changed. Unity will blindly rewrite the data on disk which will cause some
            // source control systems to check-out the files. If we are just updating
            // the content to the same content, we do not want to cause an accidental
            // checkout.

            if (AreListsDifferent(obj.SerializedStateKeys, serializedKeys))
            {
                obj.SerializedStateKeys = serializedKeys;
            }
            if (AreListsDifferent(obj.SerializedStateValues, serializedValues))
            {
                obj.SerializedStateValues = serializedValues;
            }
            if (AreListsDifferent(obj.SerializedObjectReferences, serializationOperator.SerializedObjects))
            {
                obj.SerializedObjectReferences = serializationOperator.SerializedObjects;
            }

            // Calling SetDirty seems cause prefab instances to have prefab differences after a script
            // reload, so we only call SetDirty on ScriptableObjects (it's only really necessary for those as well)
            if (obj is ScriptableObject)
            {
                fiLateBindings.EditorUtility.SetDirty((ScriptableObject)obj);
            }

            if (callbacks != null)
            {
                callbacks.OnAfterSerialize();
            }

            return(success);
        }
        /// <summary>
        /// Determines whether the specified object is equal to this one.
        /// </summary>
        public bool Equals(InspectedProperty p)
        {
            // If parameter is null return false:
            if ((object)p == null) {
                return false;
            }

            // Return true if the fields match:
            return (StorageType == p.StorageType) && (Name == p.Name);
        }
Beispiel #29
0
 /// <summary>
 /// Draws a GUI for editing the given property and returns the updated
 /// value. This does
 /// *not* write the updated value to a container.
 /// </summary>
 public static object EditPropertyDirect(Rect region, InspectedProperty property, object propertyValue, fiGraphMetadataChild metadataChild)
 {
     return(EditPropertyDirect(region, property, propertyValue, metadataChild, null));
 }
 public bool IsInterested(InspectedProperty property) {
     return IsPropertyTypeInspectable(property) && ShouldDisplayProperty(property);
 }
 /// <summary>
 ///     Initializes a new instance of the <see cref="InspectedPropertyKeyBinding" /> class.
 /// </summary>
 /// <param name="inspectedProperty">
 ///     The inspected property.
 /// </param>
 /// <param name="columnBinding">
 ///     The column binding.
 /// </param>
 protected InspectedPropertyKeyBinding(InspectedProperty inspectedProperty, IColumnBinding columnBinding)
     : base(inspectedProperty.InspectedType, columnBinding)
 {
 }
        /// <summary>
        /// Returns true if the given property should be displayed in the inspector. This method
        /// assumes that the property type is inspectable.
        /// </summary>
        private static bool ShouldDisplayProperty(InspectedProperty property)
        {
            var memberInfo = property.MemberInfo;

            // If ShowInInspector is present, we will *always* display the attribute.
            if (memberInfo.IsDefined(typeof(ShowInInspectorAttribute), /*inherit:*/ true)) {
                return true;
            }

            // note: we do opt-out serialization annotations before opt-in annotations
            //       so that we can still serialize a field but not display it in the
            //       inspector (as the serialize annotations automatically cause a field
            //       to be displayed)
            if (memberInfo.IsDefined(typeof(HideInInspector), /*inherit:*/ true) ||
                memberInfo.IsDefined(typeof(NotSerializedAttribute), /*inherit:*/true) ||
                fiInstalledSerializerManager.SerializationOptOutAnnotations.Any(t => memberInfo.IsDefined(t, /*inherit*/true))) {

                return false;
            }

            if (property.IsStatic == false && fiInstalledSerializerManager.SerializationOptInAnnotations.Any(t => memberInfo.IsDefined(t, /*inherit*/true))) {
                return true;
            }

            if (property.MemberInfo is PropertyInfo && fiSettings.InspectorRequireShowInInspector) {
                return false;
            }

            return
                // IsSerializedByFullInspector will return false for BaseObject types, so we want to
                // special case support for them being inspected.
                typeof(BaseObject).Resolve().IsAssignableFrom(property.StorageType.Resolve()) ||
                InspectedType.IsSerializedByFullInspector(property) ||
                InspectedType.IsSerializedByUnity(property);
        }
Beispiel #33
0
        public void Write(object context, object value)
        {
            if (byType != null)
            {
                if (value != s_RemoveObject)
                {
                    value = InspectedType.Get((Type)value).CreateInstance();
                }
                byType[byType.Length - 1].Write(context, value);
                return;
            }

            // The property was potentially found on a different type. We need to
            // update it to associate with this type. It's very possible the
            // property will not even apply to context, in which case Write
            // becomes a no-op.
            InspectedProperty propertyToUse = byProperty;

            if (byProperty.MemberInfo.DeclaringType != context.GetType())
            {
                var childProp = InspectedType.Get(context.GetType()).GetPropertyByName(byProperty.Name);
                if (childProp != null)
                {
                    propertyToUse = childProp;
                }
                else
                {
                    return;
                }
            }

            if (byListIndex >= 0)
            {
                var read = propertyToUse.Read(context);
                var list = (IList)read;

                var elementType = InspectedType.Get(propertyToUse.StorageType).ElementType;

                if (value == s_RemoveObject)
                {
                    fiListUtility.RemoveAt(ref list, elementType, byListIndex);
                }
                else
                {
                    while (byListIndex >= list.Count)
                    {
                        fiListUtility.Add(ref list, elementType);
                    }
                    list[byListIndex] = value;
                }

                // Changing list will not update the actual array reference, so
                // we have to write back to the stored object.
                if (list is Array)
                {
                    propertyToUse.Write(context, list);
                }

                return;
            }

            if (byDictKey != null)
            {
                var read = propertyToUse.Read(context);
                var dict = (IDictionary)read;

                // TODO: Have a separate Write/Remove command, since you might
                //       want to set a dict field to null.
                if (value == s_RemoveObject)
                {
                    dict.Remove(byDictKey);
                }
                else
                {
                    dict[byDictKey] = value;
                }

                return;
            }

            if (value != s_RemoveObject)
            {
                propertyToUse.Write(context, value);
            }
            else
            {
                propertyToUse.Write(context, null);
            }
        }
Beispiel #34
0
        /// <summary>
        /// Returns true if the given property on the given object instance has a
        /// prefab override.
        /// </summary>
        /// <param name="instance">The object instance.</param>
        /// <param name="property">The property to check.</param>
        /// <returns>
        /// True if the property is prefab override, false otherwise.
        /// </returns>
        /// <remarks>
        /// Currently, this method only works for MonoBehavior targets.
        /// </remarks>
        public static bool HasPrefabDiff(object instance, InspectedProperty property)
        {
            // For prefab differences, we rely upon the internal Unity mechanisms
            // for identifying when an object has a prefab diff. We are able to
            // do this because we only support top-level prefab differences.
            //
            // One of the current issues with this mechanism is when an array is
            // serialized by Unity, and only part of the array tracks the prefab,
            // then the inspector will show the entire array in bold (when only
            // the one part should be).

            // We only want top-level components
            if (instance is MonoBehaviour == false)
            {
                return(false);
            }

            // If there is no prefab, then we don't show anything in bold.
            var prefabGameObject = (GameObject)PrefabUtility.GetPrefabParent(((MonoBehaviour)instance).gameObject);

            if (prefabGameObject == null)
            {
                return(false);
            }

            // If the prefab doesn't have this component, then the entire
            // component should be in bold.
            var prefab = prefabGameObject.GetComponent(instance.GetType());

            if (prefab == null)
            {
                return(true);
            }

            // Get all of the property modifications on the object. If there are
            // no property modifications, then nothing should be in bold.
            PropertyModification[] mods = PrefabUtility.GetPropertyModifications((UnityObject)instance);
            if (mods == null)
            {
                return(false);
            }

            var serializedInstance = instance as ISerializedObject;

            if (serializedInstance != null)
            {
                foreach (PropertyModification mod in mods)
                {
                    // A property modification can take one of two forms. It can
                    // either be modifying a Unity serialized value or a Full
                    // Inspector serialized value.

                    // Check to see if it's a Full Inspector serialized value. If
                    // it is, then we lookup the key that the modification is
                    // associated with and, if we find said key, and that the key
                    // is equal to the property we are checking for, then we
                    // return true.
                    string serializedPropertyName;
                    if (TryExtractPropertyName(serializedInstance, mod, out serializedPropertyName) &&
                        serializedPropertyName == property.Name)
                    {
                        return(true);
                    }

                    // Check to see if it is a Unity serialized value. We have to
                    // do a dotted comparison because the propertyPath may be
                    // associated with, ie, an array, which in that case the path
                    // is something like "values.Array._items[0]" while
                    // property.Name is just "values".
                    if (ContainsPropertyName(mod.propertyPath, property.Name))
                    {
                        return(true);
                    }
                }
            }

            return(false);
        }
Beispiel #35
0
        /// <summary>
        /// Reverts the given property on the instance to the prefab value.
        /// </summary>
        /// <param name="instance">
        /// The prefab instance to revert the value on.
        /// </param>
        /// <param name="property">The property to revert.</param>
        public static void RevertValue(object instance,
                                       InspectedProperty property)
        {
            // We only want top-level components
            if (instance is MonoBehaviour == false)
            {
                return;
            }

            // Not a prefab
            var prefabGameObject = (GameObject)PrefabUtility.GetPrefabParent(((MonoBehaviour)instance).gameObject);

            if (prefabGameObject == null)
            {
                return;
            }

            // Get all of the property modifications on the object. If there are
            // no property modifications, then there is nothing to revert.
            PropertyModification[] mods = PrefabUtility.GetPropertyModifications((UnityObject)instance);
            if (mods == null)
            {
                return;
            }

            ISerializedObject serializedInstance = (ISerializedObject)instance;

            bool removed = false;

            for (int i = 0; i < mods.Length; ++i)
            {
                PropertyModification mod = mods[i];

                // A property modification can take one of two forms. It can
                // either be modifying a Unity serialized value or a Full
                // Inspector serialized value.

                // Check to see if it's a Full Inspector serialized value. If it
                // is, then we lookup the key that the modification is associated
                // with and, if we find said key, and that the key is equal to
                // the property we are checking for, then we return true.
                string serializedPropertyName;
                if (TryExtractPropertyName(serializedInstance, mod, out serializedPropertyName) &&
                    serializedPropertyName == property.Name)
                {
                    removed = true;
                }

                // Check to see if it is a Unity serialized value. We have to do
                // a dotted comparison because the propertyPath may be associated
                // with, ie, an array, which in that case the path is something
                // like "values.Array._items[0]" while property.Name is just
                // "values".
                if (ContainsPropertyName(mod.propertyPath, property.Name))
                {
                    removed = true;
                }

                if (removed)
                {
                    ArrayUtility.RemoveAt(ref mods, i);
                    PrefabUtility.SetPropertyModifications((UnityObject)instance, mods);
                    break;
                }
            }
        }
 public bool IsInterested(InspectedProperty property) {
     return property.IsStatic && IsPropertyTypeInspectable(property);
 }
Beispiel #37
0
        public static float EditPropertyHeight(object container, InspectedProperty property, fiGraphMetadataChild metadata)
        {
            object propertyValue = property.Read(container);

            return(EditPropertyHeightDirect(property, propertyValue, metadata));
        }
Beispiel #38
0
        public static Dictionary <ObjectDataPath[], object> Create(List <ObjectDataPath> toUs, object obj, HashSet <object> seenObjects)
        {
            /*
             * Debug.Log(string.Join(", ", toUs.Select(t => {
             *  if (t.byProperty == null) return "";
             *  return t.byProperty.Name;
             * }).ToArray()));
             * Debug.Log("seenObjects.Count = " + seenObjects.Count);
             * Debug.Log("seenObjects.Contains " + obj + " = " + seenObjects.Contains(obj));
             */

            Dictionary <ObjectDataPath[], object> thisLevel = AllocateDict();

            if (seenObjects.Add(obj) == false)
            {
                return(thisLevel);
            }

            // Write the type.
            thisLevel.Add(CreateNavigation(toUs, new ObjectDataPath(toUs.ToArray())), obj.GetType());

            List <InspectedProperty> properties = InspectedType.Get(obj.GetType()).GetProperties(InspectedMemberFilters.InspectableMembers);

            for (int i = 0; i < properties.Count; ++i)
            {
                InspectedProperty property         = properties[i];
                object            valueForProperty = property.Read(obj);

                if (IsPrimitiveValue(valueForProperty))
                {
                    thisLevel.Add(CreateNavigation(toUs, new ObjectDataPath(property)), valueForProperty);
                    continue;
                }

                if (InspectedType.Get(property.StorageType).IsCollection)
                {
                    if (valueForProperty is IList)
                    {
                        var listForProperty = (IList)valueForProperty;
                        for (int j = 0; j < listForProperty.Count; ++j)
                        {
                            object listElement = listForProperty[j];

                            if (IsPrimitiveValue(listElement))
                            {
                                thisLevel.Add(CreateNavigation(toUs, new ObjectDataPath(property, j)), listElement);
                                continue;
                            }

                            var listChildValues = CreateWithPath(toUs, new ObjectDataPath(property, j), listElement, seenObjects);
                            foreach (var entry in listChildValues)
                            {
                                thisLevel.Add(entry.Key, entry.Value);
                            }
                        }
                    }
                    else if (valueForProperty is IDictionary)
                    {
                        var dictForProperty = (IDictionary)valueForProperty;
                        IDictionaryEnumerator enumerator = dictForProperty.GetEnumerator();
                        while (enumerator.MoveNext())
                        {
                            object key   = enumerator.Key;
                            object value = enumerator.Value;

                            if (value == null || value.GetType().IsPrimitive || value is string)
                            {
                                thisLevel.Add(CreateNavigation(toUs, new ObjectDataPath(property, key)), value);
                            }
                            else
                            {
                                toUs.Add(new ObjectDataPath(property, key));
                                var dictChildValues = Create(toUs, value, seenObjects);
                                toUs.RemoveAt(toUs.Count - 1);
                                foreach (var entry in dictChildValues)
                                {
                                    thisLevel.Add(entry.Key, entry.Value);
                                }
                            }
                        }
                    }
                    else
                    {
                        Debug.LogError("Please file a bug (with an example) requesting multiedit support for " + valueForProperty.GetType().CSharpName());
                    }
                    continue;
                }

                // Navigate children.
                var childValues = CreateWithPath(toUs, new ObjectDataPath(property), valueForProperty, seenObjects);
                foreach (var entry in childValues)
                {
                    thisLevel.Add(entry.Key, entry.Value);
                }
            }

            return(thisLevel);
        }
Beispiel #39
0
 /// <summary>
 ///     Initializes a new instance of the <see cref="EntityScanTarget" /> class.
 /// </summary>
 /// <param name="property">
 ///     The inspected property.
 /// </param>
 /// <param name="entitySpec">
 ///     The entity specification.
 /// </param>
 /// <param name="target">
 ///     The scan target.
 /// </param>
 internal EntityScanTarget(InspectedProperty property, EntitySpec entitySpec, object target)
     : base(property.PropertyType, entitySpec)
 {
     this.target = target;
     this.setter = property.Setter;
 }
 public bool IsInterested(InspectedProperty property) {
     return false;
 }