Beispiel #1
0
 /// <summary>
 /// Gets a type from its <see cref="DataContractAttribute.Alias"/> or <see cref="DataAliasAttribute.Name"/>.
 /// </summary>
 /// <param name="alias"></param>
 /// <returns></returns>
 public static Type GetTypeFromAlias(string alias)
 {
     // TODO: At some point we might want to reorganize AssemblyRegistry and DataSerializerFactory
     // I am not sure the list of assemblies matches between those two (some assemblies are probably not registered in AssemblyRegistry),
     // so the semantic of GetTypeFromAlias (which include all assemblies) might be different than GetType.
     return(DataSerializerFactory.GetTypeFromAlias(alias));
 }
Beispiel #2
0
        public override UpdatableMember ResolveIndexer(string indexerName)
        {
            // Note: we currently only support component with data contract aliases
            var dotIndex = indexerName.LastIndexOf('.');

            // TODO: Temporary hack to get static field of the requested type/property name
            // Need to have access to DataContract name<=>type mapping in the runtime (only accessible in SiliconStudio.Core.Design now)
            var typeName = (dotIndex == -1) ? indexerName : indexerName.Substring(0, dotIndex);
            var type     = DataSerializerFactory.GetTypeFromAlias(typeName);

            if (type == null)
            {
                throw new InvalidOperationException($"Can't find a type with alias {typeName}; did you properly set a DataContractAttribute with this alias?");
            }

            return(new EntityComponentPropertyAccessor(type));
        }
Beispiel #3
0
        /// <summary>
        /// Compiles a list of update operations into a <see cref="CompiledUpdate"/>, for use with <see cref="Run"/>.
        /// </summary>
        /// <param name="rootObjectType">The type of the root object.</param>
        /// <param name="animationPaths">The different paths and source offsets to use when <see cref="Run"/> is applied.</param>
        /// <returns>A <see cref="CompiledUpdate"/> object that can be used for <see cref="Run"/>.</returns>
        public static CompiledUpdate Compile(Type rootObjectType, List <UpdateMemberInfo> animationPaths)
        {
            var currentPath          = string.Empty;
            var temporaryObjectsList = new List <object>();

            var state = new ComputeUpdateOperationState();

            state.UpdateOperations = new List <UpdateOperation>();
            state.StackPath        = new List <AnimationBuilderStackEntry>
            {
                new AnimationBuilderStackEntry(rootObjectType, 0, 0, -1),
            };

            foreach (var animationPath in animationPaths)
            {
                var commonPathParts = 1;

                // Detect how much of the path is unmodified (note: we start from 1 because root always stay there)
                for (int index = 1; index < state.StackPath.Count; index++)
                {
                    var pathPart = state.StackPath[index];

                    // Check if next path part is the same (first check length then content)
                    if (((animationPath.Name.Length == pathPart.EndIndex) ||
                         (animationPath.Name.Length > pathPart.EndIndex && (animationPath.Name[pathPart.EndIndex] == PathDelimiter || animationPath.Name[pathPart.EndIndex] == PathIndexerOpen))) &&
                        string.Compare(animationPath.Name, pathPart.StartIndex, currentPath, pathPart.StartIndex, pathPart.EndIndex - pathPart.StartIndex, StringComparison.Ordinal) == 0)
                    {
                        commonPathParts++;
                        continue;
                    }

                    break;
                }

                PopObjects(ref state, commonPathParts);

                // Parse the new path parts
                state.ParseElementStart = state.StackPath.Last().EndIndex;

                // Compute offset from start of current object
                state.NewOffset = state.StackPath.Last().ObjectStartOffset;

                while (state.ParseElementStart < animationPath.Name.Length)
                {
                    var containerType = state.StackPath.Last().Type;

                    // We have only two cases for now: array or property/field name
                    bool isIndexerAccess = animationPath.Name[state.ParseElementStart] == PathIndexerOpen;
                    if (isIndexerAccess)
                    {
                        // Parse until end of indexer
                        state.ParseElementEnd = animationPath.Name.IndexOf(PathIndexerClose, state.ParseElementStart + 1);
                        if (state.ParseElementEnd == -1)
                        {
                            throw new InvalidOperationException("Property path parse error: could not find indexer end ']'");
                        }

                        // Include the indexer close
                        state.ParseElementEnd++;

                        // Parse integer
                        // TODO: Avoid substring?
                        var indexerString = animationPath.Name.Substring(state.ParseElementStart + 1, state.ParseElementEnd - state.ParseElementStart - 2);

                        // T[], IList<T>, etc...
                        // Try to resolve using custom resolver
                        UpdatableMember updatableMember = null;
                        var             parentType      = containerType;
                        while (parentType != null)
                        {
                            UpdateMemberResolver resolver;
                            if (MemberResolvers.TryGetValue(parentType, out resolver))
                            {
                                updatableMember = resolver.ResolveIndexer(indexerString);
                                if (updatableMember != null)
                                {
                                    break;
                                }
                            }

                            parentType = parentType.GetTypeInfo().BaseType;
                        }

                        // Try interfaces
                        if (updatableMember == null)
                        {
                            foreach (var implementedInterface in containerType.GetTypeInfo().ImplementedInterfaces)
                            {
                                UpdateMemberResolver resolver;
                                if (MemberResolvers.TryGetValue(implementedInterface, out resolver))
                                {
                                    updatableMember = resolver.ResolveIndexer(indexerString);
                                    if (updatableMember != null)
                                    {
                                        break;
                                    }
                                }
                            }
                        }

                        if (updatableMember == null)
                        {
                            throw new InvalidOperationException(string.Format("Property path parse error: could not find binding info for index {0} in type {1}", indexerString, containerType));
                        }

                        ProcessMember(ref state, animationPath, updatableMember, temporaryObjectsList);
                    }
                    else
                    {
                        // Note: first character might be a . delimiter, if so, skip it
                        var propertyStart = state.ParseElementStart;
                        if (animationPath.Name[propertyStart] == PathDelimiter)
                        {
                            propertyStart++;
                        }

                        // Check if it started with a parenthesis (to perform a cast operation)
                        if (animationPath.Name[propertyStart] == PathCastOpen)
                        {
                            // Parse until end of cast operation
                            state.ParseElementEnd = animationPath.Name.IndexOf(PathCastClose, ++propertyStart);
                            if (state.ParseElementEnd == -1)
                            {
                                throw new InvalidOperationException("Property path parse error: could not find cast operation ending ')'");
                            }

                            var typeName = animationPath.Name.Substring(propertyStart, state.ParseElementEnd - propertyStart);

                            // Include the indexer close
                            state.ParseElementEnd++;

                            // Try to resolve using alias first, then full assembly registry using assembly qualified name
                            var type = DataSerializerFactory.GetTypeFromAlias(typeName) ?? AssemblyRegistry.GetType(typeName);
                            if (type == null)
                            {
                                throw new InvalidOperationException($"Could not resolve type {typeName}");
                            }

                            // Push entry with new type
                            // TODO: Should we actually perform an early castclass and skip if type is incorrect?
                            state.StackPath.Add(new AnimationBuilderStackEntry(type, state.ParseElementStart, state.ParseElementEnd, -1)
                            {
                                ObjectStartOffset = state.NewOffset,
                            });
                        }
                        else
                        {
                            UpdatableMember updatableMember;

                            // Parse until end next group (or end)
                            state.ParseElementEnd = animationPath.Name.IndexOfAny(PathGroupDelimiters, state.ParseElementStart + 1);
                            if (state.ParseElementEnd == -1)
                            {
                                state.ParseElementEnd = animationPath.Name.Length;
                            }

                            // TODO: Avoid substring?
                            var propertyName = animationPath.Name.Substring(propertyStart, state.ParseElementEnd - propertyStart);
                            if (!UpdateKeys.TryGetValue(new UpdateKey(containerType, propertyName), out updatableMember))
                            {
                                // Try to resolve using custom resolver
                                var parentType = containerType;
                                while (parentType != null)
                                {
                                    UpdateMemberResolver resolver;
                                    if (MemberResolvers.TryGetValue(parentType, out resolver))
                                    {
                                        updatableMember = resolver.ResolveProperty(propertyName);
                                        if (updatableMember != null)
                                        {
                                            break;
                                        }
                                    }

                                    parentType = parentType.GetTypeInfo().BaseType;
                                }

                                if (updatableMember == null)
                                {
                                    throw new InvalidOperationException(string.Format("Property path parse error: could not find binding info for member {0} in type {1}", propertyName, containerType));
                                }
                            }

                            // Process member
                            ProcessMember(ref state, animationPath, updatableMember, temporaryObjectsList);
                        }
                    }

                    state.ParseElementStart = state.ParseElementEnd;
                }

                currentPath = animationPath.Name;
            }

            // Totally pop the stack (we might still have stuff to copy back into properties
            PopObjects(ref state, 0);

            return(new CompiledUpdate
            {
                TemporaryObjects = temporaryObjectsList.ToArray(),
                UpdateOperations = state.UpdateOperations.ToArray(),
            });
        }