class XPathUtil {

    public static StepList steps(final AttributeDescriptor root, final String xpathExpression,
            final NamespaceSupport namespaces) throws IllegalArgumentException {

        if (root == null) {
            throw new NullPointerException("root");
        }

        if (xpathExpression == null) {
            throw new NullPointerException("xpathExpression");
        }

        String expression = xpathExpression.trim();

        if ("".equals(expression)) {
            throw new IllegalArgumentException("expression is empty");
        }

        StepList steps = new StepList();

        if ("/".equals(expression)) {
            expression = root.getName().getLocalPart();
        }

        if (expression.startsWith("/")) {
            expression = expression.substring(1);
        }

        final List<String> partialSteps = splitPath(expression);

        if (partialSteps.size() == 0) {
            throw new IllegalArgumentException("no steps provided");
        }

        int startIndex = 0;

        for (int i = startIndex; i < partialSteps.size(); i++) {

            String step = partialSteps.get(i);
            if ("..".equals(step)) {
                steps.remove(steps.size() - 1);
            } else if (".".equals(step)) {
                continue;
            } else {
                int index = 1;
                boolean isXmlAttribute = false;
                boolean isIndexed = false;
                String predicate = null;
                String stepName = step;
                if (step.indexOf('[') != -1) {
                    int start = step.indexOf('[');
                    int end = step.indexOf(']');
                    stepName = step.substring(0, start);
                    String s = step.substring(start + 1, end);
                    Scanner scanner = new Scanner(s);
                    if (scanner.hasNextInt()) {
                        index = scanner.nextInt();
                        isIndexed = true;
                    } else {
                        predicate = s;
                    }
                }
                if (step.charAt(0) == '@') {
                    isXmlAttribute = true;
                    stepName = stepName.substring(1);
                }
                QName qName = deglose(stepName, root, namespaces, isXmlAttribute);
                if (predicate == null) {
                    steps.add(new Step(qName, index, isXmlAttribute, isIndexed));
                } else {
                    steps.add(new Step(qName, isXmlAttribute, predicate));
                }
                 
            }
            //            
            // if (step.indexOf('[') != -1) {
            // int start = step.indexOf('[');
            // int end = step.indexOf(']');
            // String stepName = step.substring(0, start);
            // int stepIndex = Integer.parseInt(step.substring(start + 1, end));
            // QName qName = deglose(stepName, root, namespaces);
            // steps.add(new Step(qName, stepIndex));
            // } else if ("..".equals(step)) {
            // steps.remove(steps.size() - 1);
            // } else if (".".equals(step)) {
            // continue;
            // } else {
            // QName qName = deglose(step, root, namespaces);
            // steps.add(new Step(qName, 1));
            // }
        }
        
        // XPath simplification phase: if the xpath expression contains more
        // nodes
        // than the root node itself, and the root node is present, remove the
        // root
        // node as it is redundant
        if (root != null && steps.size() > 1) {
            Step step = (Step) steps.get(0);
            Name rootName = root.getName();
            QName stepName = step.getName();
            if (Types.equals(rootName, stepName)) {
                LOGGER.fine("removing root name from xpath " + steps + " as it is redundant");
                steps.remove(0);
            }
        }
        
        return steps;
    }

}