public static INode CreateNode <S, T>(this IType type, ObjectNode parent, string subpath,
                                       IEnvironment environment, object initialValue)
 {
     return(type.CreateNode <S, T>(parent, subpath, environment, initialValue, (child, node) => child));
 }
 public static INode CreateNode <S, T>(this IType <S, T> type, ObjectNode parent, string subpath,
                                       IEnvironment environment, object initialValue, Func <object, IStateTreeNode, object> createNewInstance, Action <INode, object, IStateTreeNode> finalizeNewInstance = null)
 {
     return((type as IType).CreateNode <S, T>(parent, subpath, environment, initialValue, createNewInstance, finalizeNewInstance));
 }
Ejemplo n.º 3
0
        public static object RunMiddlewares(ObjectNode node, IMiddlewareEvent baseCall, Func <object[], object> action)
        {
            IEnumerator <IMiddleware> middlewares = CollectMiddlewares(node).GetEnumerator();

            object result = null;

            bool nextInvoked = false;

            bool abortInvoked = false;

            return(RunMiddlewares(baseCall));

            object RunMiddlewares(IMiddlewareEvent call)
            {
                IMiddleware middleware = middlewares.MoveNext() ? middlewares.Current : null;

                void Next(IMiddlewareEvent ncall, Func <object, object> callback)
                {
                    nextInvoked = true;
                    // the result can contain
                    // - the non manipulated return value from an action
                    // - the non manipulated abort value
                    // - one of the above but manipulated through the callback function
                    if (callback != null)
                    {
                        result = callback(RunMiddlewares(ncall) ?? result);
                    }
                    else
                    {
                        result = RunMiddlewares(ncall);
                    }
                }

                void Abort(object value)
                {
                    abortInvoked = true;
                    // overwrite the result
                    // can be manipulated through middlewares earlier in the queue using the callback fn
                    result = value;
                }

                object InvokeHandler()
                {
                    middleware.Handler(call, Next, Abort);
                    var xnode = call.Tree.GetStateTreeNode();

                    if (!nextInvoked && !abortInvoked)
                    {
                        Console.WriteLine($"Neither the next() nor the abort() callback within the middleware {middleware.Handler.ToString()} for the action: '{call.Name}' on the node: {xnode.Type.Name} was invoked.");
                    }
                    if (nextInvoked && abortInvoked)
                    {
                        Console.WriteLine($"The next() and abort() callback within the middleware {middleware.Handler.ToString()} for the action: '{call.Name}' on the node: ${node.Type.Name} were invoked.");
                    }
                    return(result);
                }

                if (middleware?.Handler != null)
                {
                    if (middleware.IncludeHooks)
                    {
                        return(InvokeHandler());
                    }
                    else
                    {
                        if (Enum.TryParse(call.Name, out Hook hook))
                        {
                            return(RunMiddlewares(call));
                        }
                        else
                        {
                            return(InvokeHandler());
                        }
                    }
                }
                else
                {
                    return(Actions.RunInAction(call.Name, action, new object[] { call.Target }.Concat(call.Arguments).ToArray()));
                }
            }
        }
 public static INode CreateNode <S, T>(this IType <S, T> type, ObjectNode parent, string subpath,
                                       IEnvironment environment, object initialValue)
 {
     return((type as IType).CreateNode <S, T>(parent, subpath, environment, initialValue));
 }
 public void SetParent(ObjectNode newParent, string subpath)
 {
     // throw new InvalidOperationException("Cannot change parent of immutable node");
 }
Ejemplo n.º 6
0
        public ObjectNode(IType type,
                          ObjectNode parent, string subpath,
                          IEnvironment environment,
                          object initialValue, object storedValue,
                          bool canAttachTreeNode,
                          Action <INode, object> finalize)
        {
            NodeId = ++NextNodeId;

            Type = type;

            StoredValue = storedValue;

            _SubPath = ObservableValue <string> .From();

            Subpath = subpath;

            _Parent = ObservableValue <ObjectNode> .From();

            Parent = parent;

            Environment = environment;

            _IsRunningAction = false;

            IsProtectionEnabled = true;

            AutoUnbox = true;

            State = NodeLifeCycle.INITIALIZING;

            PreBoot();

            PreCompute();

            if (parent == null)
            {
                IdentifierCache = new IdentifierCache();
            }

            if (canAttachTreeNode)
            {
                NodeCache.Add(StoredValue, new StateTreeNode(this));
            }

            bool sawException = true;

            try
            {
                _IsRunningAction = true;

                finalize?.Invoke(this, initialValue);

                _IsRunningAction = false;

                if (parent != null)
                {
                    parent.Root.IdentifierCache?.AddNodeToCache(this);
                }
                else
                {
                    IdentifierCache?.AddNodeToCache(this);
                }

                FireHook("afterCreate");

                State = NodeLifeCycle.CREATED;

                sawException = false;
            }
            finally
            {
                if (sawException)
                {
                    // short-cut to die the instance, to avoid the snapshot computed starting to throw...
                    State = NodeLifeCycle.DEAD;
                }
            }

            var disposer = Reactions.Reaction <object>((r) => Snapshot, (snapshot, r) => EmitSnapshot(snapshot));

            AddDisposer(() => disposer.Dispose());
        }
Ejemplo n.º 7
0
        public static List <INode> ReconcileListItems <Sx, Tx>(ObjectNode parent, IType <Sx, Tx> type, List <INode> oldNodes, object[] newValues, string[] newPaths)
        {
            INode oldNode, oldMatch;

            object newValue;

            bool hasNewNode = false;

            for (int i = 0; ; i++)
            {
                hasNewNode = i <= newValues.Length - 1;
                oldNode    = i < oldNodes.Count ? oldNodes[i] : null;
                newValue   = hasNewNode ? newValues[i] : null;

                // for some reason, instead of newValue we got a node, fallback to the storedValue
                // TODO: https://github.com/mobxjs/mobx-state-tree/issues/340#issuecomment-325581681
                if (StateTreeUtils.IsNode(newValue) || newValue is TempNode)
                {
                    newValue = (newValue as INode).StoredValue;
                }

                // both are empty, end
                if (oldNode == null && !hasNewNode)
                {
                    break;
                    // new one does not exists, old one dies
                }
                else if (!hasNewNode)
                {
                    oldNode.Dispose();
                    oldNodes.Splice(i, 1);
                    i--;
                    // there is no old node, create it
                }
                else if (oldNode == null)
                {
                    // check if already belongs to the same parent. if so, avoid pushing item in. only swapping can occur.
                    if (newValue.IsStateTreeNode() && newValue.GetStateTreeNode().Parent == parent)
                    {
                        // this node is owned by this parent, but not in the reconcilable set, so it must be double
                        throw new Exception($"Cannot add an object to a state tree if it is already part of the same or another state tree.Tried to assign an object to '{parent.Path}/{newPaths[i]}', but it lives already at '{newValue.GetStateTreeNode().Path}'");
                    }
                    oldNodes.Splice(i, 0, ValueAsNode(type, parent, newPaths[i], newValue));
                }
                else if (AreSame(oldNode, newValue)) // both are the same, reconcile
                {
                    oldNodes[i] = ValueAsNode(type, parent, newPaths[i], newValue, oldNode);
                    // nothing to do, try to reorder
                }
                else
                {
                    oldMatch = null;

                    // find a possible candidate to reuse
                    for (int j = i; j < oldNodes.Count; j++)
                    {
                        if (AreSame(oldNodes[j], newValue))
                        {
                            oldMatch = oldNodes.Splice(j, 1)[0];
                            break;
                        }
                    }

                    oldNodes.Splice(i, 0, ValueAsNode(type, parent, newPaths[i], newValue, oldMatch));
                }
            }

            return(oldNodes);
        }
Ejemplo n.º 8
0
 public static INode ValueAsNode <Sx, Tx>(IType <Sx, Tx> type, ObjectNode parent, string subpath, object value)
 {
     return(ValueAsNode(type, parent, subpath, value, null));
 }
        public static INode ResolveNodeByPaths(this ObjectNode bnode, string[] paths, bool failIfResolveFails = true)
        {
            // counter part of getRelativePath
            // note that `../` is not part of the JSON pointer spec, which is actually a prefix format
            // in json pointer: "" = current, "/a", attribute a, "/" is attribute "" etc...
            // so we treat leading ../ apart...

            INode current = bnode;

            for (int i = 0; i < paths.Length; i++)
            {
                var path = paths[i];
                if (path == "")
                {
                    current = current?.Root;
                    continue;
                }
                else if (path == "..")
                {
                    current = current?.Parent;
                    if (current != null)
                    {
                        // not everything has a parent
                        continue;
                    }
                }
                else if (path == "." || path == "")
                {
                    // '/bla' or 'a//b' splits to empty strings
                    continue;
                }
                else if (current != null)
                {
                    if (current is ScalarNode)
                    {
                        // check if the value of a scalar resolves to a state tree node (e.g. references)
                        // then we can continue resolving...
                        try
                        {
                            var value = current.Value;
                            if (value.IsStateTreeNode())
                            {
                                current = value.GetStateTreeNode();
                                // fall through
                            }
                        }
                        catch
                        {
                            if (!failIfResolveFails)
                            {
                                return(null);
                            }
                            throw;
                        }
                    }

                    if (current is ObjectNode)
                    {
                        var subType = (current as ObjectNode).GetChildType(path);
                        if (subType != null)
                        {
                            current = (current as ObjectNode).GetChildNode(path);
                            if (current != null)
                            {
                                continue;
                            }
                        }
                    }
                }

                if (failIfResolveFails)
                {
                    var join = paths.Take(i).JoinJsonPath();

                    var value = string.IsNullOrWhiteSpace(join) ? "/" : join;

                    throw new Exception($"Could not resolve '{path}' in path '{value}' while resolving '{paths.JoinJsonPath()}'");
                }
                else
                {
                    return(null);
                }
            }
            return(current);
        }
 public static INode ResolveNodeByPaths(this ObjectNode bnode, IEnumerable <string> paths, bool failIfResolveFails = true)
 {
     return(bnode.ResolveNodeByPaths(paths.ToArray(), failIfResolveFails));
 }
 public static INode ResolveNodeByPath(this ObjectNode bnode, string path, bool failIfResolveFails = true)
 {
     return(bnode.ResolveNodeByPaths(path.SplitJsonPath(), failIfResolveFails));
 }