nsresult
txXPathOptimizer::optimizePath(Expr* aInExpr, Expr** aOutExpr)
{
    PathExpr* path = static_cast<PathExpr*>(aInExpr);

    uint32_t i;
    Expr* subExpr;
    // look for steps like "//foo" that can be turned into "/descendant::foo"
    // and "//." that can be turned into "/descendant-or-self::node()"
    for (i = 0; (subExpr = path->getSubExprAt(i)); ++i) {
        if (path->getPathOpAt(i) == PathExpr::DESCENDANT_OP &&
            subExpr->getType() == Expr::LOCATIONSTEP_EXPR &&
            !subExpr->getSubExprAt(0)) {
            LocationStep* step = static_cast<LocationStep*>(subExpr);
            if (step->getAxisIdentifier() == LocationStep::CHILD_AXIS) {
                step->setAxisIdentifier(LocationStep::DESCENDANT_AXIS);
                path->setPathOpAt(i, PathExpr::RELATIVE_OP);
            }
            else if (step->getAxisIdentifier() == LocationStep::SELF_AXIS) {
                step->setAxisIdentifier(LocationStep::DESCENDANT_OR_SELF_AXIS);
                path->setPathOpAt(i, PathExpr::RELATIVE_OP);
            }
        }
    }

    // look for expressions that start with a "./"
    subExpr = path->getSubExprAt(0);
    LocationStep* step;
    if (subExpr->getType() == Expr::LOCATIONSTEP_EXPR &&
        path->getSubExprAt(1) &&
        path->getPathOpAt(1) != PathExpr::DESCENDANT_OP) {
        step = static_cast<LocationStep*>(subExpr);
        if (step->getAxisIdentifier() == LocationStep::SELF_AXIS &&
            !step->getSubExprAt(0)) {
            txNodeTest* test = step->getNodeTest();
            txNodeTypeTest* typeTest;
            if (test->getType() == txNodeTest::NODETYPE_TEST &&
                (typeTest = static_cast<txNodeTypeTest*>(test))->
                  getNodeTestType() == txNodeTypeTest::NODE_TYPE) {
                // We have a '.' as first step followed by a single '/'.

                // Check if there are only two steps. If so, return the second
                // as resulting expression.
                if (!path->getSubExprAt(2)) {
                    *aOutExpr = path->getSubExprAt(1);
                    path->setSubExprAt(1, nullptr);

                    return NS_OK;
                }

                // Just delete the '.' step and leave the rest of the PathExpr
                path->deleteExprAt(0);
            }
        }
    }

    return NS_OK;
}
nsresult
txXPathOptimizer::optimizeUnion(Expr* aInExpr, Expr** aOutExpr)
{
    UnionExpr* uni = static_cast<UnionExpr*>(aInExpr);

    // Check for expressions like "foo | bar" and
    // "descendant::foo | descendant::bar"

    nsresult rv;
    uint32_t current;
    Expr* subExpr;
    for (current = 0; (subExpr = uni->getSubExprAt(current)); ++current) {
        if (subExpr->getType() != Expr::LOCATIONSTEP_EXPR ||
            subExpr->getSubExprAt(0)) {
            continue;
        }

        LocationStep* currentStep = static_cast<LocationStep*>(subExpr);
        LocationStep::LocationStepType axis = currentStep->getAxisIdentifier();

        txUnionNodeTest* unionTest = nullptr;

        // Check if there are any other steps with the same axis and merge
        // them with currentStep
        uint32_t i;
        for (i = current + 1; (subExpr = uni->getSubExprAt(i)); ++i) {
            if (subExpr->getType() != Expr::LOCATIONSTEP_EXPR ||
                subExpr->getSubExprAt(0)) {
                continue;
            }

            LocationStep* step = static_cast<LocationStep*>(subExpr);
            if (step->getAxisIdentifier() != axis) {
                continue;
            }
            
            // Create a txUnionNodeTest if needed
            if (!unionTest) {
                nsAutoPtr<txNodeTest> owner(unionTest = new txUnionNodeTest);
                NS_ENSURE_TRUE(unionTest, NS_ERROR_OUT_OF_MEMORY);
                
                rv = unionTest->addNodeTest(currentStep->getNodeTest());
                NS_ENSURE_SUCCESS(rv, rv);

                currentStep->setNodeTest(unionTest);
                owner.forget();
            }

            // Merge the nodetest into the union
            rv = unionTest->addNodeTest(step->getNodeTest());
            NS_ENSURE_SUCCESS(rv, rv);

            step->setNodeTest(nullptr);

            // Remove the step from the UnionExpr
            uni->deleteExprAt(i);
            --i;
        }

        // Check if all expressions were merged into a single step. If so,
        // return the step as the new expression.
        if (unionTest && current == 0 && !uni->getSubExprAt(1)) {
            // Make sure the step doesn't get deleted when the UnionExpr is
            uni->setSubExprAt(0, nullptr);
            *aOutExpr = currentStep;

            // Return right away since we no longer have a union            
            return NS_OK;
        }
    }

    return NS_OK;
}