IExpressionTreeNode InjectInto(PredicateNode targetPredicate, Func<IPredicateConnectiveNode> createConnectiveNode) { if (targetPredicate == null) throw new ArgumentNullException("targetPredicate"); var sourceParent = Parent; var targetParent = targetPredicate.Parent; if(Parent != null && targetParent != null && object.ReferenceEquals(Parent, targetParent)) { Parent.SwapChildren(); return FindRoot(); } if(targetParent == null) { // create a parent for a target var newParent = createConnectiveNode(); newParent.AssignChild(targetPredicate, ChildNodePosition.Right); targetPredicate.AssignParent(newParent); targetParent = newParent; } //# get target sibling // injection will be handled differently depending on type of the sibling var targetPosition = targetParent.GetChildPosition(targetPredicate); var targetSibling = (IExpressionTreeNode)null; if (targetPosition == ChildNodePosition.Left) targetSibling = targetParent.Right; else targetSibling = targetParent.Left; if (targetSibling == null || targetSibling is PredicateNode) { // target sibling is null or predicate // we can simply replace target with a Connective Node // (if sibling was a connective node itself then injecting another connective node would create bracket in logic, which we don't want) // (node with two connective node children is treated as a bracket, e.g: AND.l = [a OR b], AND.l = [c OR d] => ((a OR b) AND (c OR d)) return InjectInto(this, targetPredicate, createConnectiveNode); } else if (targetSibling is PredicateConnectiveNode) { // target sibling is a Predicate Connective Node // predicate will be injected in a new Predicate Connective Node above target parent return InjectInto(this, targetParent as PredicateConnectiveNode, createConnectiveNode); } else { throw new NotSupportedException(); } }
IExpressionTreeNode InjectInto(PredicateNode target, Func<IPredicateConnectiveNode> createConnectiveNode) { var source = this; var targetParent = target.Parent; //# target is child of source, do nothing if (object.Equals(target.Parent, source)) return FindRoot(); //# target and this are children of same parent -> swap places if(targetParent != null && object.Equals(targetParent, source.Parent)) { targetParent.SwapChildren(); return FindRoot(); } var originalTargetPosition = default(ChildNodePosition); if(targetParent != null) { originalTargetPosition = targetParent.GetChildPosition(target); } bool targetIsDescendantOfSource = target.IsDescendantOf(source); if(targetIsDescendantOfSource) { // target is a descendant of source // before source is injected into a parent of a target (because target is a Predicate Node, source cannot be injected into it directly) // attach target parent to parent of source var targetGrandParent = targetParent.Parent; targetGrandParent.ClearChildAssignment(targetParent); targetParent.AssignParent(source.Parent); if(source.Parent != null) source.Parent.ReplaceChildNode(source, targetParent); } else { if (source.Parent != null) { source.Parent.ClearChildAssignment(source); source.AssignParent(null); } } //# check if source has an empty child slot if(source.Left == null || source.Right == null) { // connect target to source // connect source to target parent source.AssignParent(targetParent); targetParent.ReplaceChildNode(target, source); target.AssignParent(source); if (source.Left == null) source.AssignChild(target, ChildNodePosition.Left); else source.AssignChild(target, ChildNodePosition.Right); return FindRoot(); } //# source has both children, create a new connective mode to link target parent, target and source var newConnective = createConnectiveNode(); newConnective.CopyFrom(this); newConnective.AssignChild(target, ChildNodePosition.Left); newConnective.AssignChild(this, ChildNodePosition.Right); target.AssignParent(newConnective); targetParent.AssignChild(newConnective, originalTargetPosition); newConnective.AssignParent(targetParent); //# update this parent if (this.Parent != null) { this.Parent.ClearChildAssignment(this); } this.AssignParent(newConnective); return FindRoot(); }