All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.saxon.tree.util.SteppingNavigator Maven / Gradle / Ivy

There is a newer version: 12.5
Show newest version
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2017-2023 Saxonica Limited
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

package net.sf.saxon.tree.util;

import net.sf.saxon.om.NamespaceUri;
import net.sf.saxon.pattern.*;
import net.sf.saxon.tree.iter.AxisIterator;
import net.sf.saxon.type.Type;


/**
 * The SteppingNavigator is a utility class containing methods to assist with navigating a tree whose nodes
 * implement the {@link SteppingNode} interface
 */

public abstract class SteppingNavigator {

    /**
     * Get the next following node after a given node
     *
     * @param start  the starting node
     * @param anchor the node whose descendants are being scanned; the scan terminates when
     *               the anchor node is reached
     * @return the next node in document order after the starting node, excluding attributes and namespaces;
     * or null if no such node is found
     */

    @SuppressWarnings("RedundantCast")
    static SteppingNode getFollowingNode(SteppingNode start, SteppingNode anchor) {
        SteppingNode nodei = start.getFirstChild();
        if (nodei != null) {
            return nodei;
        }
        if (start.isSameNodeInfo(anchor)) {
            return null;
        }
        nodei = start;
        SteppingNode parenti = (SteppingNode)start.getParent();
        do {
            nodei = nodei.getNextSibling();
            if (nodei != null) {
                return nodei;
            } else if (parenti.isSameNodeInfo(anchor)) {
                return null;
            }
            nodei = parenti;
            parenti = (SteppingNode)parenti.getParent();
        } while (parenti != null);

        return null;
    }

    /**
     * Interface representing a function to step from one node to another within a tree
     */

    private interface Stepper {
        /**
         * Step from one node to another
         *
         * @param node the start node
         * @return the end node
         */
        SteppingNode step(SteppingNode node);
    }

    /**
     * Stepper that steps from one node in a document to the next node in document order,
     * excluding attribute and namespace nodes, returning null when the root of the subtree
     * is reached.
     */

    private static class FollowingNodeStepper implements Stepper {

        SteppingNode anchor;

        /**
         * Create a stepper to step successively through all nodes in a subtree
         *
         * @param anchor the root of the subtree, marking the end point of the iteration
         */

        FollowingNodeStepper(SteppingNode anchor) {
            this.anchor = anchor;
        }

        @Override
        public SteppingNode step(SteppingNode node) {
            return getFollowingNode(node, anchor);
        }
    }

    /**
     * Stepper that steps from one node in a document to the next node in document order,
     * excluding attribute and namespace nodes, returning null when the root of the subtree
     * is reached, and including only nodes that match a specified node test
     */

    private static class FollowingFilteredNodeStepper implements Stepper {

        SteppingNode anchor;
        NodeTest test;

        /**
         * Create a stepper to step successively through selected nodes in a subtree
         *
         * @param anchor the root of the subtree, marking the end point of the iteration
         * @param test   the test that returned nodes must satisfy
         */

        FollowingFilteredNodeStepper(SteppingNode anchor, NodeTest test) {
            this.anchor = anchor;
            this.test = test;
        }

        @Override
        public SteppingNode step(SteppingNode node) {
            do {
                node = getFollowingNode(node, anchor);
            } while (node != null && !test.test(node));
            return node;
        }
    }

    /**
     * Stepper that steps from one element in a document to the next element in document order,
     * excluding attribute and namespace nodes, returning null when the root of the subtree
     * is reached, and including only elements, with optional constraints on the namespace URI
     * and/or local name.
     */

    private static class FollowingElementStepper implements Stepper {

        SteppingNode anchor;
        NamespaceUri uri;
        String local;

        /**
         * Create a stepper to step successively through selected elements in a subtree
         *
         * @param anchor the root of the subtree, marking the end point of the iteration
         * @param uri    either null, or a namespace URI which the selected elements must match
         * @param local  either null, or a local name which the selected elements must match
         */

        FollowingElementStepper(SteppingNode anchor, NamespaceUri uri, String local) {
            this.anchor = anchor;
            this.uri = uri;
            this.local = local;
        }

        @Override
        public SteppingNode step(SteppingNode node) {
            return node.getSuccessorElement(anchor, uri, local);
        }
    }

    /**
     * Stepper that steps from one element in a document to the next element in document order,
     * excluding attribute and namespace nodes, returning null when the root of the subtree
     * is reached, and including only elements, with a constraint on the fingerprint of the element
     */

    private static class FollowingFingerprintedElementStepper implements Stepper {

        SteppingNode anchor;
        int fingerprint;

        /**
         * Create a stepper to step successively through selected elements in a subtree
         *
         * @param anchor      the root of the subtree, marking the end point of the iteration
         * @param fingerprint a fingerprint which selected elements must match
         */

        FollowingFingerprintedElementStepper(SteppingNode anchor, int fingerprint) {
            this.anchor = anchor;
            this.fingerprint = fingerprint;
        }

        @Override
        public SteppingNode step(SteppingNode node) {
            do {
                node = getFollowingNode(node, anchor);
            } while (node != null && node.getFingerprint() != fingerprint);
            return node;
        }
    }


    /**
     * An iterator over the descendant or descendant-or-self axis
     */

    public static class DescendantAxisIterator implements AxisIterator {

        private final SteppingNode start;
        private SteppingNode current;
        private boolean done;
        private final Stepper stepper;

        /**
         * Create an iterator over the descendant or descendant-or-self axis
         *
         * @param start       the root of the subtree whose descendants are required
         * @param includeSelf true if this is the descendant-or-self axis
         * @param test        the node-test that selected nodes must satisfy
         */

        public DescendantAxisIterator(SteppingNode start, boolean includeSelf, NodeTest test) {
            this.start = start;

            if (!(includeSelf && test.test(start))) {
                // initialize currNode to the start node if and only if this is NOT a descendant-or-self scan
                current = start;
            }

            if (test == null || test == AnyNodeTest.getInstance()) {
                stepper = new FollowingNodeStepper(start);
            } else if (test instanceof NameTest) {
                if (((NameTest)test).getPrimitiveType() == Type.ELEMENT) {
                    NameTest nt = (NameTest) test;
                    if (start.hasFingerprint()) {
                        stepper = new FollowingFingerprintedElementStepper(start, nt.getFingerprint());
                    } else {
                        stepper = new FollowingElementStepper(start, nt.getNamespaceURI(), nt.getLocalPart());
                    }
                } else {
                    stepper = new FollowingFilteredNodeStepper(start, test);
                }
            } else if (test instanceof NodeKindTest) {
                if (((NodeKindTest)test).getPrimitiveType() == Type.ELEMENT) {
                    stepper = new FollowingElementStepper(start, null, null);
                } else {
                    stepper = new FollowingFilteredNodeStepper(start, test);
                }
            } else if (test instanceof LocalNameTest) {
                if (((LocalNameTest)test).getPrimitiveType() == Type.ELEMENT) {
                    LocalNameTest nt = (LocalNameTest) test;
                    stepper = new FollowingElementStepper(start, null, nt.getLocalName());
                } else {
                    stepper = new FollowingFilteredNodeStepper(start, test);
                }
            } else if (test instanceof NamespaceTest) {
                if (((NamespaceTest)test).getPrimitiveType() == Type.ELEMENT) {
                    NamespaceTest nt = (NamespaceTest) test;
                    stepper = new FollowingElementStepper(start, nt.getNamespaceURI(), null);
                } else {
                    stepper = new FollowingFilteredNodeStepper(start, test);
                }
            } else {
                stepper = new FollowingFilteredNodeStepper(start, test);
            }
        }


        @Override
        public SteppingNode next() {
            if (done) {
                return null;
            }
            if (current == null) {
                // implies includeSelf: first time round, return the start node
                current = start;
                return start;
            }
            SteppingNode curr = stepper.step(current);
            if (curr == null) {
                done = true;
            }
            return current = curr;
        }

    }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy