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

lux.compiler.SlopCounter Maven / Gradle / Ivy

There is a newer version: 1.1.0
Show newest version
package lux.compiler;

import lux.xml.QName;
import lux.xml.ValueType;
import lux.xpath.AbstractExpression;
import lux.xpath.BinaryOperation;
import lux.xpath.ExpressionVisitorBase;
import lux.xpath.FunCall;
import lux.xpath.NodeTest;
import lux.xpath.PathStep;
import lux.xpath.Root;
import lux.xpath.Sequence;

/**
 * adds up the number of wildcard ("*" or node()) path steps on the left or
 * right-hand side of a path; descendant axis steps count as an infinite
 * number of wildcard steps.
 
 * The path distance underlying this function expresses the "vertical"
 * distance between two sets of nodes.  This is really only defined for
 * expressions separated by some combination of self, child, and
 * descendant steps, and it counts the number of wildcard (*, node())
 * steps.
 */

public class SlopCounter extends ExpressionVisitorBase {
    
    public SlopCounter () {
    }

    private boolean done = false;
    private Integer slop = null;

    /**
     * reset back to the initial state so the counter may be reused.
     */
    public void reset() {
        slop = null;
        done = false;
    }

    @Override
    public AbstractExpression visit(Root root) {
        foundNode();
        return root;
    }

    @Override
    public AbstractExpression visit(PathStep step) {
        NodeTest nodeTest = step.getNodeTest();
        switch (step.getAxis()) {
        case Child:
            if ((nodeTest.getType().equals(ValueType.NODE) || nodeTest.getType().equals(ValueType.ELEMENT))) {
                foundNode();
                if (nodeTest.isWild()) {
                    ++slop;
                } else {
                    done = true;
                }
            } // else? done?
            break;
        case Self:
            if ((nodeTest.getType().equals(ValueType.NODE) || nodeTest.getType().equals(ValueType.ELEMENT))) {
                foundNode();
                if (!isReverse()) {
                    --slop; // self:: matches an adjacent wildcard *on the left* and closes up a gap
                }
                // however - multiple self:: in sequence ??
            } // else? done?
            break;
        case Descendant:
        case DescendantSelf:
            if (isReverse()) {
                // we're going right-to-left
                if (slop == null && !nodeTest.isWild()) {
                    // we see our first thing and it's a named node
                    slop = 0;
                    done = true;
                } else {
                    slop = 98;
                }
            } else {
                // A number bigger than any document would ever be nested?  A
                // document nested this deeply would likely cause other
                // problems.  Surround Query Parser can only parse 2-digit distances
                slop = 98;
            }
            break;
        case Attribute:
            if (nodeTest.getQName() != null) {
                foundNode();
            }
            break;
        default:
            done = true;
            break;
        }
        return step;
    }

    private void foundNode() {
        if (slop == null) {
            slop = 0;
        }
    }

    @Override
    public AbstractExpression visit(FunCall f) {
        QName name = f.getName();
        if (! (name.equals(FunCall.FN_EXISTS) || name.equals(FunCall.FN_DATA) || name.getNamespaceURI().equals(FunCall.XS_NAMESPACE))) {
            // We can infer a path relationship with exists() and data() and constructors because they are 
            // existence-preserving.  We should also be able to invert not(exists()) and 
            // empty(), and not(), etc. in the path index case.
            slop = null;
        }
        done = true;
        return f;
    }

    @Override
    public AbstractExpression visit(Sequence seq) {
    	computeMaxSubSlop(seq);
        done = true;
        return seq;
    }

    @Override
    public AbstractExpression visit(BinaryOperation exp) {
    	computeMaxSubSlop(exp);
        done = true;
        return exp;
    }
    
	private void computeMaxSubSlop(AbstractExpression exp) {
		int maxSlop = -1;
    	Integer origSlop = slop;
    	for (AbstractExpression sub : exp.getSubs()) {
    		sub.accept(this);
    		if (slop == null) {
    		    continue;
    		}
    		done = false; // child step may have indicated we're done ...
    		maxSlop = Math.max(maxSlop, slop);
    		slop = origSlop;
    	}
    	if (maxSlop >= 0) {
    		slop = maxSlop;
    	}
	}

    public Integer getSlop () {
        return slop;
    }
    
    @Override
    public boolean isDone () {
        return done;
    }


}

/* 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/. */




© 2015 - 2024 Weber Informatics LLC | Privacy Policy