protected virtual ISelectionVisitorAction Visit(
            ISelection selection,
            TContext context)
        {
            var localContext = OnBeforeEnter(selection, context);
            ISelectionVisitorAction result = Enter(selection, localContext);

            localContext = OnAfterEnter(selection, localContext, result);

            if (result.Kind == SelectionVisitorActionKind.Continue)
            {
                if (VisitChildren(selection, context).Kind == SelectionVisitorActionKind.Break)
                {
                    return(Break);
                }
            }

            if (result.Kind == SelectionVisitorActionKind.Continue ||
                result.Kind == SelectionVisitorActionKind.SkipAndLeave)
            {
                localContext = OnBeforeLeave(selection, localContext);
                result       = Leave(selection, localContext);
                OnAfterLeave(selection, localContext, result);
            }

            return(result);
        }
        protected override ISelectionVisitorAction VisitObjectType(
            IOutputField field,
            ObjectType objectType,
            SelectionSetNode?selectionSet,
            QueryableProjectionContext context)
        {
            var isAbstractType = field.Type.NamedType().IsAbstractType();

            if (isAbstractType && context.TryGetQueryableScope(out QueryableProjectionScope? scope))
            {
                context.PushInstance(
                    Expression.Convert(context.GetInstance(), objectType.RuntimeType));
                scope.Level.Push(new Queue <MemberAssignment>());

                ISelectionVisitorAction res =
                    base.VisitObjectType(field, objectType, selectionSet, context);

                context.PopInstance();
                scope.AddAbstractType(objectType.RuntimeType, scope.Level.Pop());

                return(res);
            }

            return(base.VisitObjectType(field, objectType, selectionSet, context));
        }
        protected virtual ISelectionVisitorAction VisitChildren(
            IOutputField field,
            TContext context)
        {
            IOutputType      type         = field.Type;
            SelectionSetNode?selectionSet =
                context.SelectionSetNodes.Peek();

            INamedType namedType = type.NamedType();

            if (namedType.IsAbstractType())
            {
                IReadOnlyList <ObjectType> possibleTypes =
                    context.Context.Schema.GetPossibleTypes(field.Type.NamedType());

                foreach (var possibleType in possibleTypes)
                {
                    ISelectionVisitorAction result =
                        VisitObjectType(field, possibleType, selectionSet, context);

                    if (result != Continue)
                    {
                        return(result);
                    }
                }
            }
            else if (namedType is ObjectType a)
            {
                return(VisitObjectType(field, a, selectionSet, context));
            }

            return(DefaultAction);
        }
        protected override TContext OnAfterEnter(
            ISelection selection,
            TContext localContext,
            ISelectionVisitorAction result)
        {
            if (selection is IProjectionSelection projectionSelection &&
                projectionSelection.Handler is IProjectionFieldHandler <TContext> handler)
            {
                return(handler.OnAfterEnter(localContext, selection, result));
            }

            return(localContext);
        }
        /// <inheritdoc/>
        public override bool TryHandleEnter(
            Neo4JProjectionVisitorContext context,
            ISelection selection,
            out ISelectionVisitorAction action)
        {
            IObjectField field = selection.Field;

            action = SelectionVisitor.SkipAndLeave;

            if (!context.StartNodes.Any())
            {
                context.Projections.Add(field.GetName());
                return(true);
            }

            if (context.StartNodes.Count != context.EndNodes.Count)
            {
                context.EndNodes.Push(Cypher.NamedNode(selection.DeclaringType.Name.Value));
            }

            if (context.StartNodes.Count != context.Relationships.Count)
            {
                Neo4JRelationshipAttribute rel = context.RelationshipTypes.Peek();
                Node startNode = context.StartNodes.Peek();
                Node endNode   = context.EndNodes.Peek();


                Relationship direction = rel.Direction switch
                {
                    Incoming => startNode.RelationshipFrom(endNode, rel.Name),

                    Outgoing => startNode.RelationshipTo(endNode, rel.Name),

                    None => startNode.RelationshipBetween(endNode, rel.Name),

                    _ => throw new InvalidOperationException(
                              Neo4JResources.Projection_RelationshipDirectionNotSet)
                };

                context.Relationships.Push(direction);
            }

            context
            .RelationshipProjections[context.CurrentLevel]
            .Enqueue(selection.Field.GetName());

            return(true);
        }
    }
 public T OnAfterLeave(T context, ISelection selection, ISelectionVisitorAction result) =>
 _handler.OnAfterLeave(context, selection, result);
 public virtual T OnAfterLeave(
     T context,
     ISelection selection,
     ISelectionVisitorAction action) => context;
 protected virtual TContext OnAfterLeave(
     ISelection selection,
     TContext localContext,
     ISelectionVisitorAction result) =>
 localContext;
 protected virtual TContext OnAfterEnter(
     IOutputField field,
     TContext localContext,
     ISelectionVisitorAction result) =>
 localContext;