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)); }
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"); }
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()); }
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); }
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)); }