text:
package org.codehaus.groovy.classgen.asm;

import org.codehaus.groovy.GroovyBugError;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
import org.codehaus.groovy.ast.expr.ArrayExpression;
import org.codehaus.groovy.ast.expr.BinaryExpression;
import org.codehaus.groovy.ast.expr.ClassExpression;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.ElvisOperatorExpression;
import org.codehaus.groovy.ast.expr.EmptyExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.FieldExpression;
import org.codehaus.groovy.ast.expr.ListExpression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.expr.NotExpression;
import org.codehaus.groovy.ast.expr.PostfixExpression;
import org.codehaus.groovy.ast.expr.PrefixExpression;
import org.codehaus.groovy.ast.expr.PropertyExpression;
import org.codehaus.groovy.ast.expr.TernaryExpression;
import org.codehaus.groovy.ast.expr.TupleExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.ast.tools.GeneralUtils;
import org.codehaus.groovy.ast.tools.WideningCategories;
import org.codehaus.groovy.classgen.AsmClassGenerator;
import org.codehaus.groovy.classgen.BytecodeExpression;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.syntax.Token;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;

import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
import static org.codehaus.groovy.ast.tools.GeneralUtils.binX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
import static org.codehaus.groovy.syntax.Types.ASSIGN;
import static org.codehaus.groovy.syntax.Types.BITWISE_AND;
import static org.codehaus.groovy.syntax.Types.BITWISE_AND_EQUAL;
import static org.codehaus.groovy.syntax.Types.BITWISE_OR;
import static org.codehaus.groovy.syntax.Types.BITWISE_OR_EQUAL;
import static org.codehaus.groovy.syntax.Types.BITWISE_XOR;
import static org.codehaus.groovy.syntax.Types.BITWISE_XOR_EQUAL;
import static org.codehaus.groovy.syntax.Types.COMPARE_EQUAL;
import static org.codehaus.groovy.syntax.Types.COMPARE_GREATER_THAN;
import static org.codehaus.groovy.syntax.Types.COMPARE_GREATER_THAN_EQUAL;
import static org.codehaus.groovy.syntax.Types.COMPARE_IDENTICAL;
import static org.codehaus.groovy.syntax.Types.COMPARE_LESS_THAN;
import static org.codehaus.groovy.syntax.Types.COMPARE_LESS_THAN_EQUAL;
import static org.codehaus.groovy.syntax.Types.COMPARE_NOT_EQUAL;
import static org.codehaus.groovy.syntax.Types.COMPARE_NOT_IDENTICAL;
import static org.codehaus.groovy.syntax.Types.COMPARE_NOT_IN;
import static org.codehaus.groovy.syntax.Types.COMPARE_NOT_INSTANCEOF;
import static org.codehaus.groovy.syntax.Types.COMPARE_TO;
import static org.codehaus.groovy.syntax.Types.DIVIDE;
import static org.codehaus.groovy.syntax.Types.DIVIDE_EQUAL;
import static org.codehaus.groovy.syntax.Types.ELVIS_EQUAL;
import static org.codehaus.groovy.syntax.Types.EQUAL;
import static org.codehaus.groovy.syntax.Types.FIND_REGEX;
import static org.codehaus.groovy.syntax.Types.INTDIV;
import static org.codehaus.groovy.syntax.Types.INTDIV_EQUAL;
import static org.codehaus.groovy.syntax.Types.KEYWORD_IN;
import static org.codehaus.groovy.syntax.Types.KEYWORD_INSTANCEOF;
import static org.codehaus.groovy.syntax.Types.LEFT_SHIFT;
import static org.codehaus.groovy.syntax.Types.LEFT_SHIFT_EQUAL;
import static org.codehaus.groovy.syntax.Types.LEFT_SQUARE_BRACKET;
import static org.codehaus.groovy.syntax.Types.LOGICAL_AND;
import static org.codehaus.groovy.syntax.Types.LOGICAL_OR;
import static org.codehaus.groovy.syntax.Types.MATCH_REGEX;
import static org.codehaus.groovy.syntax.Types.MINUS;
import static org.codehaus.groovy.syntax.Types.MINUS_EQUAL;
import static org.codehaus.groovy.syntax.Types.MINUS_MINUS;
import static org.codehaus.groovy.syntax.Types.MOD;
import static org.codehaus.groovy.syntax.Types.MOD_EQUAL;
import static org.codehaus.groovy.syntax.Types.MULTIPLY;
import static org.codehaus.groovy.syntax.Types.MULTIPLY_EQUAL;
import static org.codehaus.groovy.syntax.Types.PLUS;
import static org.codehaus.groovy.syntax.Types.PLUS_EQUAL;
import static org.codehaus.groovy.syntax.Types.PLUS_PLUS;
import static org.codehaus.groovy.syntax.Types.POWER;
import static org.codehaus.groovy.syntax.Types.POWER_EQUAL;
import static org.codehaus.groovy.syntax.Types.RIGHT_SHIFT;
import static org.codehaus.groovy.syntax.Types.RIGHT_SHIFT_EQUAL;
import static org.codehaus.groovy.syntax.Types.RIGHT_SHIFT_UNSIGNED;
import static org.codehaus.groovy.syntax.Types.RIGHT_SHIFT_UNSIGNED_EQUAL;
import static org.objectweb.asm.Opcodes.ACONST_NULL;
import static org.objectweb.asm.Opcodes.GOTO;
import static org.objectweb.asm.Opcodes.IFEQ;
import static org.objectweb.asm.Opcodes.IFNE;
import static org.objectweb.asm.Opcodes.INSTANCEOF;

public class BinaryExpressionHelper {
    // compare
    private static final MethodCaller compareIdenticalMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareIdentical");
    private static final MethodCaller compareNotIdenticalMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareNotIdentical");
    private static final MethodCaller compareEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareEqual");
    private static final MethodCaller compareNotEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareNotEqual");
    private static final MethodCaller compareToMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareTo");
    private static final MethodCaller compareLessThanMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareLessThan");
    private static final MethodCaller compareLessThanEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareLessThanEqual");
    private static final MethodCaller compareGreaterThanMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareGreaterThan");
    private static final MethodCaller compareGreaterThanEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareGreaterThanEqual");
    // regexp
    private static final MethodCaller findRegexMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "findRegex");
    private static final MethodCaller matchRegexMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "matchRegex");
    // isCase/isNotCase
    private static final MethodCaller isCaseMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "isCase");
    private static final MethodCaller isNotCaseMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "isNotCase");

    protected final WriterController controller;
    private final UnaryExpressionHelper unaryExpressionHelper;

    public BinaryExpressionHelper(final WriterController wc) {
        this.controller = wc;
        this.unaryExpressionHelper = new UnaryExpressionHelper(this.controller);
    }

    public WriterController getController() {
        return controller;
    }

    public MethodCaller getIsCaseMethod() {
        return isCaseMethod;
    }

    public void eval(final BinaryExpression expression) {
        switch (expression.getOperation().getType()) {
        case EQUAL: // = (aka assignment)
            evaluateEqual(expression, false);
            break;

        case COMPARE_EQUAL: // ==
            evaluateCompareExpression(compareEqualMethod, expression);
            break;

        case COMPARE_NOT_EQUAL:
            evaluateCompareExpression(compareNotEqualMethod, expression);
            break;

        case COMPARE_TO:
            evaluateCompareTo(expression);
            break;

        case COMPARE_GREATER_THAN:
            evaluateCompareExpression(compareGreaterThanMethod, expression);
            break;

        case COMPARE_GREATER_THAN_EQUAL:
            evaluateCompareExpression(compareGreaterThanEqualMethod, expression);
            break;

        case COMPARE_LESS_THAN:
            evaluateCompareExpression(compareLessThanMethod, expression);
            break;

        case COMPARE_LESS_THAN_EQUAL:
            evaluateCompareExpression(compareLessThanEqualMethod, expression);
            break;

        case LOGICAL_AND:
            evaluateLogicalAndExpression(expression);
            break;

        case LOGICAL_OR:
            evaluateLogicalOrExpression(expression);
            break;

        case BITWISE_AND:
            evaluateBinaryExpression("and", expression);
            break;

        case BITWISE_AND_EQUAL:
            evaluateBinaryExpressionWithAssignment("and", expression);
            break;

        case BITWISE_OR:
            evaluateBinaryExpression("or", expression);
            break;

        case BITWISE_OR_EQUAL:
            evaluateBinaryExpressionWithAssignment("or", expression);
            break;

        case BITWISE_XOR:
            evaluateBinaryExpression("xor", expression);
            break;

        case BITWISE_XOR_EQUAL:
            evaluateBinaryExpressionWithAssignment("xor", expression);
            break;

        case PLUS:
            evaluateBinaryExpression("plus", expression);
            break;

        case PLUS_EQUAL:
            evaluateBinaryExpressionWithAssignment("plus", expression);
            break;

        case MINUS:
            evaluateBinaryExpression("minus", expression);
            break;

        case MINUS_EQUAL:
            evaluateBinaryExpressionWithAssignment("minus", expression);
            break;

        case MULTIPLY:
            evaluateBinaryExpression("multiply", expression);
            break;

        case MULTIPLY_EQUAL:
            evaluateBinaryExpressionWithAssignment("multiply", expression);
            break;

        case DIVIDE:
            evaluateBinaryExpression("div", expression);
            break;

        case DIVIDE_EQUAL:
            //SPG don't use divide since BigInteger implements directly
            //and we want to dispatch through DefaultGroovyMethods to get a BigDecimal result
            evaluateBinaryExpressionWithAssignment("div", expression);
            break;

        case INTDIV:
            evaluateBinaryExpression("intdiv", expression);
            break;

        case INTDIV_EQUAL:
            evaluateBinaryExpressionWithAssignment("intdiv", expression);
            break;

        case MOD:
            evaluateBinaryExpression("mod", expression);
            break;

        case MOD_EQUAL:
            evaluateBinaryExpressionWithAssignment("mod", expression);
            break;

        case POWER:
            evaluateBinaryExpression("power", expression);
            break;

        case POWER_EQUAL:
            evaluateBinaryExpressionWithAssignment("power", expression);
            break;

        case ELVIS_EQUAL:
            evaluateElvisEqual(expression);
            break;

        case LEFT_SHIFT:
            evaluateBinaryExpression("leftShift", expression);
            break;

        case LEFT_SHIFT_EQUAL:
            evaluateBinaryExpressionWithAssignment("leftShift", expression);
            break;

        case RIGHT_SHIFT:
            evaluateBinaryExpression("rightShift", expression);
            break;

        case RIGHT_SHIFT_EQUAL:
            evaluateBinaryExpressionWithAssignment("rightShift", expression);
            break;

        case RIGHT_SHIFT_UNSIGNED:
            evaluateBinaryExpression("rightShiftUnsigned", expression);
            break;

        case RIGHT_SHIFT_UNSIGNED_EQUAL:
            evaluateBinaryExpressionWithAssignment("rightShiftUnsigned", expression);
            break;

        case KEYWORD_INSTANCEOF:
            evaluateInstanceof(expression);
            break;

        case COMPARE_NOT_INSTANCEOF:
            evaluateNotInstanceof(expression);
            break;

        case FIND_REGEX:
            evaluateCompareExpression(findRegexMethod, expression);
            break;

        case MATCH_REGEX:
            evaluateCompareExpression(matchRegexMethod, expression);
            break;

        case LEFT_SQUARE_BRACKET:
            if (controller.getCompileStack().isLHS()) {
                evaluateEqual(expression, false);
            } else {
                evaluateBinaryExpression("getAt", expression);
            }
            break;

        case KEYWORD_IN:
            evaluateCompareExpression(isCaseMethod, expression);
            break;

        case COMPARE_NOT_IN:
            evaluateCompareExpression(isNotCaseMethod, expression);
            break;

        case COMPARE_IDENTICAL:
            evaluateCompareExpression(compareIdenticalMethod, expression);
            break;

        case COMPARE_NOT_IDENTICAL:
            evaluateCompareExpression(compareNotIdenticalMethod, expression);
            break;

        default:
            throw new GroovyBugError("Operation: " + expression.getOperation() + " not supported");
        }
    }

    @Deprecated
    protected void assignToArray(final Expression parent, final Expression receiver, final Expression index, final Expression rhsValueLoader) {
        assignToArray(parent, receiver, index, rhsValueLoader, false);
    }

    protected void assignToArray(final Expression parent, final Expression receiver, final Expression index, final Expression rhsValueLoader, final boolean safe) {
        // let's replace this assignment to a subscript operator with a
        // method call
        // e.g. x[5] = 10
        // -> (x, [], 5), =, 10
        // -> methodCall(x, "putAt", [5, 10])
        ArgumentListExpression ae = new ArgumentListExpression(index,rhsValueLoader);
        controller.getInvocationWriter().makeCall(parent, receiver, constX("putAt"), ae, InvocationWriter.invokeMethod, safe, false, false);
        controller.getOperandStack().pop();
        // return value of assignment
        rhsValueLoader.visit(controller.getAcg());
    }

    public void evaluateElvisEqual(final BinaryExpression expression) {
        Token operation = expression.getOperation();
        BinaryExpression elvisAssignmentExpression = binX(
                expression.getLeftExpression(),
                Token.newSymbol(ASSIGN, operation.getStartLine(), operation.getStartColumn()),
                new ElvisOperatorExpression(expression.getLeftExpression(), expression.getRightExpression())
        );
        this.evaluateEqual(elvisAssignmentExpression, false);
    }

    public void evaluateEqual(final BinaryExpression expression, final boolean defineVariable) {
        AsmClassGenerator acg = controller.getAcg();
        CompileStack compileStack = controller.getCompileStack();
        OperandStack operandStack = controller.getOperandStack();
        Expression leftExpression = expression.getLeftExpression();
        Expression rightExpression = expression.getRightExpression();
        boolean directAssignment = defineVariable && !(leftExpression instanceof TupleExpression);

        if (directAssignment && rightExpression instanceof EmptyExpression) {
            VariableExpression ve = (VariableExpression) leftExpression;
            BytecodeVariable var = compileStack.defineVariable(ve, controller.getTypeChooser().resolveType(ve, controller.getClassNode()), false);
            operandStack.loadOrStoreVariable(var, false);
            return;
        }

        // let's evaluate the RHS and store the result
        ClassNode lhsType = controller.getTypeChooser().resolveType(leftExpression, controller.getClassNode());
        if (rightExpression instanceof ListExpression && lhsType.isArray()) {
            ListExpression list = (ListExpression) rightExpression;
            ArrayExpression array = new ArrayExpression(lhsType.getComponentType(), list.getExpressions());
            array.setSourcePosition(list);
            array.visit(acg);
        } else if (rightExpression instanceof EmptyExpression) {
            loadInitValue(leftExpression.getType()); // TODO: lhsType?
        } else {
            rightExpression.visit(acg);
        }

        ClassNode rhsType = operandStack.getTopOperand();
        int rhsValueId;

        if (directAssignment) {
            VariableExpression var = (VariableExpression) leftExpression;
            if (var.isClosureSharedVariable() && ClassHelper.isPrimitiveType(rhsType)) {
                // GROOVY-5570: if a closure shared variable is a primitive type, it must be boxed
                rhsType = ClassHelper.getWrapper(rhsType);
                operandStack.box();
            }

            // ensure we try to unbox null to cause a runtime NPE in case we assign
            // null to a primitive typed variable, even if it is used only in boxed
            // form as it is closure shared
            if (var.isClosureSharedVariable() && ClassHelper.isPrimitiveType(var.getOriginType()) && AsmClassGenerator.isNullConstant(rightExpression)) {
                operandStack.doGroovyCast(var.getOriginType());
                // these two are never reached in bytecode and only there
                // to avoid verifyerrors and compiler infrastructure hazzle
                operandStack.box();
                operandStack.doGroovyCast(lhsType);
            }
            // normal type transformation
            if (!ClassHelper.isPrimitiveType(lhsType) && AsmClassGenerator.isNullConstant(rightExpression)) {
                operandStack.replace(lhsType);
            } else {
                operandStack.doGroovyCast(lhsType);
            }
            rhsType = lhsType;
            rhsValueId = compileStack.defineVariable(var, lhsType, true).getIndex();
        } else {
            rhsValueId = compileStack.defineTemporaryVariable("$rhs", rhsType, true);
        }
        // TODO: if rhs is VariableSlotLoader already, then skip crating a new one
        BytecodeExpression rhsValueLoader = new VariableSlotLoader(rhsType,rhsValueId,operandStack);

        // assignment for subscript
        if (leftExpression instanceof BinaryExpression) {
            BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
            if (leftBinExpr.getOperation().getType() == LEFT_SQUARE_BRACKET) {
                assignToArray(expression, leftBinExpr.getLeftExpression(), leftBinExpr.getRightExpression(), rhsValueLoader, leftBinExpr.isSafe());
            }
            compileStack.removeVar(rhsValueId);
            return;
        }

        compileStack.pushLHS(true);

        if (leftExpression instanceof TupleExpression) {
            // multiple declaration
            TupleExpression tuple = (TupleExpression) leftExpression;
            int i = 0;
            for (Expression e : tuple.getExpressions()) {
                VariableExpression var = (VariableExpression) e;
                MethodCallExpression call = new MethodCallExpression(
                        rhsValueLoader, "getAt",
                        new ArgumentListExpression(new ConstantExpression(i)));
                call.visit(acg);
                i += 1;
                if (defineVariable) {
                    operandStack.doGroovyCast(var);
                    compileStack.defineVariable(var, true);
                    operandStack.remove(1);
                } else {
                    acg.visitVariableExpression(var);
                }
            }
        } else if (defineVariable) {
            // single declaration
            rhsValueLoader.visit(acg);
            operandStack.remove(1);
            compileStack.popLHS();
            return;
        } else {
            // normal assignment
            int mark = operandStack.getStackLength();
            // to leave a copy of the rightExpression value on the stack after the assignment.
            rhsValueLoader.visit(acg);
            TypeChooser typeChooser = controller.getTypeChooser();
            ClassNode targetType = typeChooser.resolveType(leftExpression, controller.getClassNode());
            operandStack.doGroovyCast(targetType);
            leftExpression.visit(acg);
            operandStack.remove(operandStack.getStackLength() - mark);
        }
        compileStack.popLHS();

        // return value of assignment
        rhsValueLoader.visit(acg);
        compileStack.removeVar(rhsValueId);
    }

    private void loadInitValue(final ClassNode type) {
        MethodVisitor mv = controller.getMethodVisitor();
        if (ClassHelper.isPrimitiveType(type)) {
            mv.visitLdcInsn(0);
        } else {
            mv.visitInsn(ACONST_NULL);
        }
        controller.getOperandStack().push(type);
    }

    protected void evaluateCompareExpression(final MethodCaller compareMethod, final BinaryExpression expression) {
        ClassNode classNode = controller.getClassNode();
        Expression leftExp = expression.getLeftExpression();
        Expression rightExp = expression.getRightExpression();
        ClassNode leftType = controller.getTypeChooser().resolveType(leftExp, classNode);
        ClassNode rightType = controller.getTypeChooser().resolveType(rightExp, classNode);

        boolean done = false;
        if (ClassHelper.isPrimitiveType(leftType) && ClassHelper.isPrimitiveType(rightType)) {
            BinaryExpressionMultiTypeDispatcher helper = new BinaryExpressionMultiTypeDispatcher(controller);
            done = helper.doPrimitiveCompare(leftType, rightType, expression);
        }
        if (!done) {
            AsmClassGenerator acg = controller.getAcg();
            OperandStack operandStack = controller.getOperandStack();

            leftExp.visit(acg);
            operandStack.box();
            rightExp.visit(acg);
            operandStack.box();

            compareMethod.call(controller.getMethodVisitor());
            ClassNode resType = ClassHelper.boolean_TYPE;
            if (compareMethod == findRegexMethod) {
                resType = ClassHelper.OBJECT_TYPE;
            }
            operandStack.replace(resType, 2);
        }
    }

    private void evaluateCompareTo(final BinaryExpression expression) {
        AsmClassGenerator acg = controller.getAcg();
        MethodVisitor mv = controller.getMethodVisitor();
        OperandStack operandStack = controller.getOperandStack();

        expression.getLeftExpression().visit(acg);
        operandStack.box();

        // if the right hand side is a boolean expression, we need to autobox
        expression.getRightExpression().visit(acg);
        operandStack.box();

        compareToMethod.call(mv);
        operandStack.replace(ClassHelper.Integer_TYPE, 2);
    }

    private void evaluateLogicalAndExpression(final BinaryExpression expression) {
        AsmClassGenerator acg = controller.getAcg();
        MethodVisitor mv = controller.getMethodVisitor();
        OperandStack operandStack = controller.getOperandStack();

        expression.getLeftExpression().visit(acg);
        operandStack.doGroovyCast(ClassHelper.boolean_TYPE);
        Label falseCase = operandStack.jump(IFEQ);

        expression.getRightExpression().visit(acg);
        operandStack.doGroovyCast(ClassHelper.boolean_TYPE);
        operandStack.jump(IFEQ, falseCase);

        ConstantExpression.PRIM_TRUE.visit(acg);
        Label trueCase = new Label();
        mv.visitJumpInsn(GOTO, trueCase);

        mv.visitLabel(falseCase);
        ConstantExpression.PRIM_FALSE.visit(acg);

        mv.visitLabel(trueCase);
        operandStack.remove(1); // have to remove 1 because of the GOTO
    }

    private void evaluateLogicalOrExpression(final BinaryExpression expression) {
        AsmClassGenerator acg = controller.getAcg();
        MethodVisitor mv = controller.getMethodVisitor();
        OperandStack operandStack = controller.getOperandStack();

        expression.getLeftExpression().visit(acg);
        operandStack.doGroovyCast(ClassHelper.boolean_TYPE);
        Label trueCase = operandStack.jump(IFNE);

        expression.getRightExpression().visit(acg);
        operandStack.doGroovyCast(ClassHelper.boolean_TYPE);
        Label falseCase = operandStack.jump(IFEQ);

        mv.visitLabel(trueCase);
        ConstantExpression.PRIM_TRUE.visit(acg);
        Label end = new Label();
        operandStack.jump(GOTO, end);

        mv.visitLabel(falseCase);
        ConstantExpression.PRIM_FALSE.visit(acg);

        mv.visitLabel(end);
    }

    protected void evaluateBinaryExpression(final String message, final BinaryExpression expression) {
        CompileStack compileStack = controller.getCompileStack();
        // ensure VariableArguments are read, not stored
        compileStack.pushLHS(false);
        controller.getInvocationWriter().makeSingleArgumentCall(
                expression.getLeftExpression(),
                message,
                expression.getRightExpression(),
                expression.isSafe()
        );
        compileStack.popLHS();
    }

    protected void evaluateArrayAssignmentWithOperator(final String method, final BinaryExpression expression, final BinaryExpression leftBinExpr) {
        // e.g. x[a] += b
        // to avoid loading x and a twice we transform the expression to use
        // ExpressionAsVariableSlot
        // -> subscript=a, receiver=x, receiver[subscript]+b, =, receiver[subscript]
        // -> subscript=a, receiver=x, receiver#getAt(subscript)#plus(b), =, receiver#putAt(subscript)
        // -> subscript=a, receiver=x, receiver#putAt(subscript, receiver#getAt(subscript)#plus(b))
        // the result of x[a] += b is x[a]+b, thus:
        // -> subscript=a, receiver=x, receiver#putAt(subscript, ret=receiver#getAt(subscript)#plus(b)), ret
        ExpressionAsVariableSlot subscript = new ExpressionAsVariableSlot(controller, leftBinExpr.getRightExpression(), "subscript");
        ExpressionAsVariableSlot receiver  = new ExpressionAsVariableSlot(controller, leftBinExpr.getLeftExpression(), "receiver");
        MethodCallExpression getAt = callX(receiver, "getAt", args(subscript));
        MethodCallExpression operation = callX(getAt, method, expression.getRightExpression());
        ExpressionAsVariableSlot ret = new ExpressionAsVariableSlot(controller, operation, "ret");
        MethodCallExpression putAt = callX(receiver, "putAt", args(subscript, ret));

        AsmClassGenerator acg = controller.getAcg();
        putAt.visit(acg);
        OperandStack os = controller.getOperandStack();
        os.pop();
        os.load(ret.getType(), ret.getIndex());

        CompileStack compileStack = controller.getCompileStack();
        compileStack.removeVar(ret.getIndex());
        compileStack.removeVar(subscript.getIndex());
        compileStack.removeVar(receiver.getIndex());
    }

    protected void evaluateBinaryExpressionWithAssignment(final String method, final BinaryExpression expression) {
        Expression leftExpression = expression.getLeftExpression();
        if (leftExpression instanceof BinaryExpression) {
            BinaryExpression bexp = (BinaryExpression) leftExpression;
            if (bexp.getOperation().getType() == LEFT_SQUARE_BRACKET) {
                evaluateArrayAssignmentWithOperator(method, expression, bexp);
                return;
            }
        }

        evaluateBinaryExpression(method, expression);

        // br to leave a copy of rvalue on the stack; see also isPopRequired()
        controller.getOperandStack().dup();
        controller.getCompileStack().pushLHS(true);
        leftExpression.visit(controller.getAcg());
        controller.getCompileStack().popLHS();
    }

    private void evaluateInstanceof(final BinaryExpression expression) {
        expression.getLeftExpression().visit(controller.getAcg());
        controller.getOperandStack().box();
        Expression rightExp = expression.getRightExpression();
        if (!(rightExp instanceof ClassExpression)) {
            throw new RuntimeException("RHS of the instanceof keyword must be a class name, not: " + rightExp);
        }
        String classInternalName = BytecodeHelper.getClassInternalName(rightExp.getType());
        controller.getMethodVisitor().visitTypeInsn(INSTANCEOF, classInternalName);
        controller.getOperandStack().replace(ClassHelper.boolean_TYPE);
    }

    private void evaluateNotInstanceof(final BinaryExpression expression) {
        unaryExpressionHelper.writeNotExpression(
                new NotExpression(
                        new BinaryExpression(
                                expression.getLeftExpression(),
                                GeneralUtils.INSTANCEOF,
                                expression.getRightExpression()
                        )
                )
        );
    }

    private void evaluatePostfixMethod(final int op, final String method, final Expression expression, final Expression orig) {
        CompileStack compileStack = controller.getCompileStack();
        OperandStack operandStack = controller.getOperandStack();

        // load Expressions
        VariableSlotLoader usesSubscript = loadWithSubscript(expression);

        // save copy for later
        operandStack.dup();
        ClassNode expressionType = operandStack.getTopOperand();
        int tempIdx = compileStack.defineTemporaryVariable("postfix_" + method, expressionType, true);

        // execute method
        execMethodAndStoreForSubscriptOperator(op, method, expression, usesSubscript, orig);

        // remove the result of the method call
        operandStack.pop();

        // reload saved value
        operandStack.load(expressionType, tempIdx);
        compileStack.removeVar(tempIdx);
        if (usesSubscript != null) compileStack.removeVar(usesSubscript.getIndex());
    }

    public void evaluatePostfixMethod(final PostfixExpression expression) {
        int op = expression.getOperation().getType();
        switch (op) {
            case PLUS_PLUS:
                evaluatePostfixMethod(op, "next", expression.getExpression(), expression);
                break;
            case MINUS_MINUS:
                evaluatePostfixMethod(op, "previous", expression.getExpression(), expression);
                break;
        }
    }

    public void evaluatePrefixMethod(final PrefixExpression expression) {
        int type = expression.getOperation().getType();
        switch (type) {
            case PLUS_PLUS:
                evaluatePrefixMethod(type, "next", expression.getExpression(), expression);
                break;
            case MINUS_MINUS:
                evaluatePrefixMethod(type, "previous", expression.getExpression(), expression);
                break;
        }
    }

    private void evaluatePrefixMethod(final int op, final String method, final Expression expression, final Expression orig) {
        // load expressions
        VariableSlotLoader usesSubscript = loadWithSubscript(expression);

        // execute method
        execMethodAndStoreForSubscriptOperator(op, method, expression, usesSubscript, orig);

        // new value is already on stack, so nothing to do here
        if (usesSubscript != null) controller.getCompileStack().removeVar(usesSubscript.getIndex());
    }

    private VariableSlotLoader loadWithSubscript(final Expression expression) {
        AsmClassGenerator acg = controller.getAcg();
        // if we have a BinaryExpression, check if it is with subscription
        if (expression instanceof BinaryExpression) {
            BinaryExpression bexp = (BinaryExpression) expression;
            if (bexp.getOperation().getType() == LEFT_SQUARE_BRACKET) {
                // right expression is the subscript expression
                // we store the result of the subscription on the stack
                Expression subscript = bexp.getRightExpression();
                subscript.visit(acg);
                OperandStack operandStack = controller.getOperandStack();
                ClassNode subscriptType = operandStack.getTopOperand();
                int id = controller.getCompileStack().defineTemporaryVariable("$subscript", subscriptType, true);
                VariableSlotLoader subscriptExpression = new VariableSlotLoader(subscriptType, id, operandStack);
                BinaryExpression rewrite = binX(bexp.getLeftExpression(), bexp.getOperation(), subscriptExpression);
                rewrite.copyNodeMetaData(bexp);
                rewrite.setSourcePosition(bexp);
                rewrite.visit(acg);
                return subscriptExpression;
            }
        }

        // normal loading of expression
        expression.visit(acg);
        return null;
    }

    private void execMethodAndStoreForSubscriptOperator(final int op, String method, final Expression expression, final VariableSlotLoader usesSubscript, final Expression orig) {
        writePostOrPrefixMethod(op, method, expression, orig);

        // we need special code for arrays to store the result (like for a[1]++)
        if (usesSubscript != null) {
            BinaryExpression be = (BinaryExpression) expression;
            CompileStack compileStack = controller.getCompileStack();
            OperandStack operandStack = controller.getOperandStack();
            ClassNode methodResultType = operandStack.getTopOperand();
            int resultIdx = compileStack.defineTemporaryVariable("postfix_" + method, methodResultType, true);
            BytecodeExpression methodResultLoader = new VariableSlotLoader(methodResultType, resultIdx, operandStack);

            // execute the assignment, this will leave the right side (here the method call result) on the stack
            assignToArray(be, be.getLeftExpression(), usesSubscript, methodResultLoader, be.isSafe());

            compileStack.removeVar(resultIdx);

        } else if (expression instanceof VariableExpression || expression instanceof PropertyExpression || expression instanceof FieldExpression) {
            // here we handle a++ and a.b++
            controller.getOperandStack().dup();
            controller.getCompileStack().pushLHS(true);
            expression.visit(controller.getAcg());
            controller.getCompileStack().popLHS();
        }
        // other cases don't need storing, so nothing to be done for them
    }

    protected void writePostOrPrefixMethod(final int op, final String method, final Expression expression, final Expression orig) {
        // at this point the receiver will be already on the stack
        // in a[1]++ the method will be "++" aka "next" and the receiver a[1]
        ClassNode exprType = controller.getTypeChooser().resolveType(expression, controller.getClassNode());
        Expression callSiteReceiverSwap = new BytecodeExpression(exprType) {
            @Override
            public void visit(MethodVisitor mv) {
                OperandStack operandStack = controller.getOperandStack();
                // CallSite is normally not showing up on the
                // operandStack, so we place a dummy here with same
                // slot length.
                operandStack.push(ClassHelper.OBJECT_TYPE);
                // change (receiver,callsite) to (callsite,receiver)
                operandStack.swap();

                setType(operandStack.getTopOperand());

                // no need to keep any of those on the operand stack
                // after this expression is processed, the operand stack
                // will contain callSiteReceiverSwap.getType()
                operandStack.remove(2);
            }
        };
        // execute method
        // this will load the callsite and the receiver normally in the wrong
        // order since the receiver is already present, but before the callsite
        // Therefore we use callSiteReceiverSwap to correct the order.
        // After this call the JVM operand stack will contain the result of
        // the method call... usually simply Object in operandStack
        controller.getCallSiteWriter().makeCallSite(
                callSiteReceiverSwap,
                method,
                MethodCallExpression.NO_ARGUMENTS,
                false, false, false, false);
        // now rhs is completely done and we need only to store. In a[1]++ this
        // would be a.getAt(1).next() for the rhs, "lhs" code is a.putAt(1, rhs)
    }

    private void evaluateElvisOperatorExpression(final ElvisOperatorExpression expression) {
        MethodVisitor mv = controller.getMethodVisitor();
        CompileStack compileStack = controller.getCompileStack();
        OperandStack operandStack = controller.getOperandStack();
        TypeChooser typeChooser = controller.getTypeChooser();

        Expression boolPart = expression.getBooleanExpression().getExpression();
        Expression falsePart = expression.getFalseExpression();

        ClassNode truePartType = typeChooser.resolveType(boolPart, controller.getClassNode());
        ClassNode falsePartType = typeChooser.resolveType(falsePart, controller.getClassNode());
        ClassNode common = WideningCategories.lowestUpperBound(truePartType, falsePartType);

        // x?:y is equal to x?x:y, which evals to
        //      var t=x; boolean(t)?t:y
        // first we load x, dup it, convert the dupped to boolean, then
        // jump depending on the value. For true we are done, for false we
        // have to load y, thus we first remove x and then load y.
        // But since x and y may have different stack lengths, this cannot work
        // Thus we have to have to do the following:
        // Be X the type of x, Y the type of y and S the common supertype of
        // X and Y, then we have to see x?:y as
        //      var t=x;boolean(t)?S(t):S(y)
        // so we load x, dup it, store the value in a local variable (t), then
        // do boolean conversion. In the true part load t and cast it to S,
        // in the false part load y and cast y to S

        // load x, dup it, store one in $t and cast the remaining one to boolean
        int mark = operandStack.getStackLength();
        boolPart.visit(controller.getAcg());
        operandStack.dup();
        if (ClassHelper.isPrimitiveType(truePartType) && !ClassHelper.isPrimitiveType(operandStack.getTopOperand())) {
            truePartType = ClassHelper.getWrapper(truePartType);
        }
        int retValueId = compileStack.defineTemporaryVariable("$t", truePartType, true);
        operandStack.castToBool(mark, true);

        Label l0 = operandStack.jump(IFEQ);
        // true part: load $t and cast to S
        operandStack.load(truePartType, retValueId);
        operandStack.doGroovyCast(common);
        Label l1 = new Label();
        mv.visitJumpInsn(GOTO, l1);

        // false part: load false expression and cast to S
        mv.visitLabel(l0);
        falsePart.visit(controller.getAcg());
        operandStack.doGroovyCast(common);

        // finish and cleanup
        mv.visitLabel(l1);
        compileStack.removeVar(retValueId);
        operandStack.replace(common, 2);
    }

    private void evaluateNormalTernary(final TernaryExpression expression) {
        MethodVisitor mv = controller.getMethodVisitor();
        TypeChooser typeChooser = controller.getTypeChooser();
        OperandStack operandStack = controller.getOperandStack();

        Expression boolPart = expression.getBooleanExpression();
        Expression truePart = expression.getTrueExpression();
        Expression falsePart = expression.getFalseExpression();

        ClassNode truePartType = typeChooser.resolveType(truePart, controller.getClassNode());
        ClassNode falsePartType = typeChooser.resolveType(falsePart, controller.getClassNode());
        ClassNode common = WideningCategories.lowestUpperBound(truePartType, falsePartType);

        // we compile b?x:y as
        //      boolean(b)?S(x):S(y), S = common super type of x,y
        // so we load b, do boolean conversion.
        // In the true part load x and cast it to S,
        // in the false part load y and cast y to S

        // load b and convert to boolean
        int mark = operandStack.getStackLength();
        boolPart.visit(controller.getAcg());
        operandStack.castToBool(mark, true);

        Label l0 = operandStack.jump(IFEQ);
        // true part: load x and cast to S
        truePart.visit(controller.getAcg());
        operandStack.doGroovyCast(common);
        Label l1 = new Label();
        mv.visitJumpInsn(GOTO, l1);

        // false part: load y and cast to S
        mv.visitLabel(l0);
        falsePart.visit(controller.getAcg());
        operandStack.doGroovyCast(common);

        // finish and cleanup
        mv.visitLabel(l1);
        operandStack.replace(common, 2);
    }

    public void evaluateTernary(final TernaryExpression expression) {
        if (expression instanceof ElvisOperatorExpression) {
            evaluateElvisOperatorExpression((ElvisOperatorExpression) expression);
        } else {
            evaluateNormalTernary(expression);
        }
    }
}


text:
#include "condor_common.h"
#if defined (HAVE_EXT_LIBVIRT) && !defined(VMWARE_ONLY)

#include "condor_config.h"
#include "condor_string.h"
#include "string_list.h"
#include "condor_attributes.h"
#include "condor_classad.h"
#include "condor_mkstemp.h"
#include "MyString.h"
#include "util_lib_proto.h"
#include "vmgahp_common.h"
#include "vm_type.h"
#include "xen_type.linux.h"
#include "vmgahp_error_codes.h"
#include "condor_vm_universe_types.h"
#include "my_popen.h"
#include <string>
#include <libvirt/libvirt.h>
#include <libvirt/virterror.h>

#define XEN_CONFIG_FILE_NAME "xen_vm.config"
#define XEN_CKPT_TIMESTAMP_FILE_SUFFIX ".timestamp"
#define XEN_MEM_SAVED_FILE "xen.mem.ckpt"
#define XEN_CKPT_TIMESTAMP_FILE XEN_MEM_SAVED_FILE XEN_CKPT_TIMESTAMP_FILE_SUFFIX

extern VMGahp *vmgahp;

VirshType::VirshType(const char* workingpath,ClassAd* ad) : VMType("", "none", workingpath, ad)
{
	m_cputime_before_suspend = 0;
	m_xen_hw_vt = false;
	m_allow_hw_vt_suspend = false;
	m_restart_with_ckpt = false;
	m_has_transferred_disk_file = false;
	m_libvirt_connection = NULL;
}

VirshType::~VirshType()
{
	priv_state old_priv = set_user_priv();
	Shutdown();
	set_priv( old_priv );

	if( getVMStatus() != VM_STOPPED ) {
		// To make sure VM exits
		killVM();
	}
	setVMStatus(VM_STOPPED);

	XenDisk *disk = NULL;
	m_disk_list.Rewind();
	while( m_disk_list.Next(disk) ) {
		m_disk_list.DeleteCurrent();
		delete disk;
	}
}

void
VirshType::Config()
{
	char *config_value = NULL;

	config_value = param("VM_NETWORKING_BRIDGE_INTERFACE");
	if( config_value ) {
		m_vm_bridge_interface = delete_quotation_marks(config_value).Value();
		free(config_value);
	} else if( vmgahp->m_gahp_config->m_vm_networking_types.contains("bridge") == true) {
		vmprintf( D_ALWAYS, "ERROR: 'VM_NETWORKING_TYPE' contains "
				"'bridge' but VM_NETWORKING_BRIDGE_INTERFACE "
				"isn't defined, so 'bridge' "
				"networking is disabled\n");
		vmgahp->m_gahp_config->m_vm_networking_types.remove("bridge");
		if( vmgahp->m_gahp_config->m_vm_networking_types.isEmpty() ) {
			vmprintf( D_ALWAYS, "ERROR: 'VM_NETWORKING' is true "
					"but 'VM_NETWORKING_TYPE' contains "
					"no valid entries, so 'VM_NETWORKING' "
					"is disabled\n");
			vmgahp->m_gahp_config->m_vm_networking = false;
		} else {
			vmprintf( D_ALWAYS,
					"Setting default networking type to 'nat'\n");
			vmgahp->m_gahp_config->m_vm_default_networking_type = "nat";
		}
	}
}

bool
VirshType::Start()
{
	vmprintf(D_FULLDEBUG, "Inside VirshType::Start\n");

	if( (m_configfile.Length() == 0)) {

	        m_result_msg = VMGAHP_ERR_INTERNAL;
		vmprintf(D_FULLDEBUG, "Config file was not set configfile: %s\n", m_configfile.Value());
		return false;
	}

	if( getVMStatus() != VM_STOPPED ) {
		m_result_msg = VMGAHP_ERR_VM_INVALID_OPERATION;
		return false;
	}

	if( m_restart_with_ckpt ) {
		bool res = Resume();
		if( res ) {
			// Success!!
			vmprintf(D_ALWAYS, "Succeeded to restart with checkpointed files\n");

			// Here we manually update timestamp of all writable disk files
			m_start_time.getTime();
			return true;
		}else {
			// Failed to restart with checkpointed files
			vmprintf(D_ALWAYS, "Failed to restart with checkpointed files\n");
			vmprintf(D_ALWAYS, "So, we will try to create a new configuration file\n");

			deleteNonTransferredFiles();
			m_configfile = "";
			m_suspendfile = "";
			m_restart_with_ckpt = false;

			if( this->CreateConfigFile() == false ) {
				vmprintf(D_ALWAYS, "Failed to create a new configuration files\n");
				return false;
			}

			// Succeeded to create a configuration file
			// Keep going..
		}
	}
	vmprintf(D_FULLDEBUG, "Trying XML: %s\n", m_xml.Value());
	priv_state priv = set_root_priv();
	virDomainPtr vm = virDomainCreateXML(m_libvirt_connection, m_xml.Value(), 0);
	set_priv(priv);

	if(vm == NULL)
	  {
	    // Error in creating the vm; let's find out what the error
	    // was
	    virErrorPtr err = virConnGetLastError(m_libvirt_connection);
	    vmprintf(D_ALWAYS, "Failed to create libvirt domain: %s\n", (err ? err->message : "No reason found"));
	    //virFreeError(err);
	    return false;
	  }

	priv = set_root_priv();
	virDomainFree(vm);
	set_priv(priv);

	setVMStatus(VM_RUNNING);
	m_start_time.getTime();
	m_cpu_time = 0;

	// Here we manually update timestamp of all writable disk files
	return true;
}

bool
VirshType::Shutdown()
{
	vmprintf(D_FULLDEBUG, "Inside VirshType::Shutdown\n");

	if( (m_configfile.Length() == 0) ) {
		m_result_msg = VMGAHP_ERR_INTERNAL;
		return false;
	}

	if( getVMStatus() == VM_STOPPED ) {
		if( m_self_shutdown ) {
			if( m_vm_no_output_vm ) {
				// A job user doesn't want to get back VM files.
				// So we will delete all files in working directory.
				m_delete_working_files = true;
				m_is_checkpointed = false;
			}else {
				if( !m_suspendfile.IsEmpty() ) {
					unlink(m_suspendfile.Value());
				}
				m_suspendfile = "";

				// delete the created xen vm configuration file
				if( !m_configfile.IsEmpty() ) {
					unlink(m_configfile.Value());
				}

				// delete the checkpoint timestamp file
				MyString tmpfilename;
				tmpfilename.sprintf("%s%c%s", m_workingpath.Value(),
						DIR_DELIM_CHAR, XEN_CKPT_TIMESTAMP_FILE);
				unlink(tmpfilename.Value());

				// We need to update timestamp of transferred writable disk files
				updateLocalWriteDiskTimestamp(time(NULL));
			}
		}
		// We here set m_self_shutdown to false
		// So, above functions will not be called twice
		m_self_shutdown = false;
		return true;
	}

	// if( getVMStatus() == VM_SUSPENDED )
	// do nothing

	// If a VM is soft suspended, resume it first
	ResumeFromSoftSuspend();

	if( getVMStatus() == VM_RUNNING ) {
		priv_state priv = set_root_priv();
                virDomainPtr dom = virDomainLookupByName(m_libvirt_connection, m_vm_name.Value());
		set_priv(priv);
		if(dom == NULL)
		  {
		    virErrorPtr err = virConnGetLastError(m_libvirt_connection);
		    if (err && err->code != VIR_ERR_NO_DOMAIN)
		      {
			vmprintf(D_ALWAYS, "Error finding domain %s: %s\n", m_vm_name.Value(), (err ? err->message : "No reason found"));
			return false;
		      }
		  }
		else
		  {
		    priv = set_root_priv();
		    int result = virDomainShutdown(dom);
		    virDomainFree(dom);
		    set_priv(priv);
		    if( result != 0 ) {
			    // system error happens
			    // killing VM by force
			    killVM();
		    }
		  }
		// Now we don't need working files any more
		m_delete_working_files = true;
		m_is_checkpointed = false;
	}

	setVMStatus(VM_STOPPED);
	m_stop_time.getTime();
	return true;
}

bool
VirshType::Checkpoint()
{
	vmprintf(D_FULLDEBUG, "Inside VirshType::Checkpoint\n");

	if( (m_configfile.Length() == 0)) {
		m_result_msg = VMGAHP_ERR_INTERNAL;
		return false;
	}

	if( getVMStatus() == VM_STOPPED ) {
		vmprintf(D_ALWAYS, "Checkpoint is called for a stopped VM\n");
		m_result_msg = VMGAHP_ERR_VM_INVALID_OPERATION;
		return false;
	}

	if( !m_vm_checkpoint ) {
		vmprintf(D_ALWAYS, "Checkpoint is not supported.\n");
		m_result_msg = VMGAHP_ERR_VM_NO_SUPPORT_CHECKPOINT;
		return false;
	}

	if( m_xen_hw_vt && !m_allow_hw_vt_suspend ) {
		// This VM uses hardware virtualization.
		// However, Virsh cannot suspend this type of VM yet.
		// So we cannot checkpoint this VM.
		vmprintf(D_ALWAYS, "Checkpoint of Hardware VT is not supported.\n");
		m_result_msg = VMGAHP_ERR_VM_NO_SUPPORT_CHECKPOINT;
		return false;
	}

	// If a VM is soft suspended, resume it first
	ResumeFromSoftSuspend();

	// This function cause a running VM to be suspended.
	if( createCkptFiles() == false ) {
		m_result_msg = VMGAHP_ERR_VM_CANNOT_CREATE_CKPT_FILES;
		vmprintf(D_ALWAYS, "failed to create checkpoint files\n");
		return false;
	}

	return true;
}

// I really need a good way to determine the type of a classad
// attribute.  Right now I just try all four possibilities, which is a
// horrible mess...
bool VirshType::CreateVirshConfigFile(const char*  /*filename*/)
{
  vmprintf(D_FULLDEBUG, "In VirshType::CreateVirshConfigFile\n");
  //  std::string name;
  char * tmp = param("LIBVIRT_XML_SCRIPT");
  if(tmp == NULL)
    {
      vmprintf(D_ALWAYS, "LIBVIRT_XML_SCRIPT not defined\n");
      return false;
    }
  // This probably needs some work...
  ArgList args;
  args.AppendArg(tmp);
  free(tmp);

  // We might want to have specific debugging output enabled in the
  // helper script; however, it is not clear where that output should
  // go.  This gives us a way to do so even in cases where the script
  // is unable to read from condor_config (why would this ever
  // happen?)
  tmp = param("LIBVIRT_XML_SCRIPT_ARGS");
  if(tmp != NULL)
    {
      MyString errormsg;
      args.AppendArgsV1RawOrV2Quoted(tmp,&errormsg);
      free(tmp);
    }
  StringList input_strings, output_strings, error_strings;
  MyString classad_string;
  m_classAd.sPrint(classad_string);
  classad_string += VMPARAM_XEN_BOOTLOADER;
  classad_string += " = \"";
  classad_string += m_xen_bootloader;
  classad_string += "\"\n";
  if(classad_string.find(VMPARAM_XEN_INITRD) < 1)
    {
      classad_string += VMPARAM_XEN_INITRD;
      classad_string += " = \"";
      classad_string += m_xen_initrd_file;
      classad_string += "\"\n";
    }
  if(!m_vm_bridge_interface.empty())
    {
      classad_string += VMPARAM_BRIDGE_INTERFACE;
      classad_string += " = \"";
      classad_string += m_vm_bridge_interface.c_str();
      classad_string += "\"\n";
    }
  if(classad_string.find(ATTR_JOB_VM_NETWORKING_TYPE) < 1)
    {
      classad_string += ATTR_JOB_VM_NETWORKING_TYPE;
      classad_string += " = \"";
      classad_string += m_vm_networking_type.Value();
      classad_string += "\"\n";
    }
  input_strings.append(classad_string.Value());

  tmp = input_strings.print_to_string();
  vmprintf(D_FULLDEBUG, "LIBVIRT_XML_SCRIPT_ARGS input_strings= %s\n", tmp);
  free(tmp);

  int ret = systemCommand(args, PRIV_ROOT, &output_strings, &input_strings, &error_strings, false);
  error_strings.rewind();
  if(ret != 0)
    {
      vmprintf(D_ALWAYS, "XML helper script could not be executed\n");
      output_strings.rewind();
      // If there is any output from the helper, write it to the debug
      // log.  Presumably, this is separate from the script's own
      // debug log.
      while((tmp = error_strings.next()) != NULL)
	{
	  vmprintf(D_FULLDEBUG, "Helper stderr output: %s\n", tmp);
	}
      return false;
    }
  error_strings.rewind();
  while((tmp = error_strings.next()) != NULL)
    {
      vmprintf(D_ALWAYS, "Helper stderr output: %s\n", tmp);
    }
  output_strings.rewind();
  while((tmp = output_strings.next()) != NULL)
    {
      m_xml += tmp;
    }
  return true;
}

bool
VirshType::ResumeFromSoftSuspend(void)
{
	vmprintf(D_FULLDEBUG, "Inside VirshType::ResumeFromSoftSuspend\n");
	if( (m_configfile.Length() == 0)) {
		return false;
	}

	if( m_is_soft_suspended ) {

		priv_state priv = set_root_priv();
		virDomainPtr dom = virDomainLookupByName(m_libvirt_connection, m_vm_name.Value());
		set_priv(priv);
		if(dom == NULL)
		  {
		    virErrorPtr err = virConnGetLastError(m_libvirt_connection);
		    vmprintf(D_ALWAYS, "Error finding domain %s: %s\n", m_vm_name.Value(), (err ? err->message : "No reason found"));
		    return false;
		  }

		priv = set_root_priv();
		int result = virDomainResume(dom);
		virDomainFree(dom);
		set_priv(priv);
		if( result != 0 ) {
			// unpause failed.
			vmprintf(D_ALWAYS, "Unpausing VM failed in "
					"VirshType::ResumeFromSoftSuspend\n");
			return false;
		}
		m_is_soft_suspended = false;
	}
	return true;
}

bool
VirshType::SoftSuspend()
{
	vmprintf(D_FULLDEBUG, "Inside VirshType::SoftSuspend\n");

	if( (m_configfile.Length() == 0)) {
		m_result_msg = VMGAHP_ERR_INTERNAL;
		return false;
	}

	if( m_is_soft_suspended ) {
		return true;
	}

	if( getVMStatus() != VM_RUNNING ) {
		m_result_msg = VMGAHP_ERR_VM_INVALID_OPERATION;
		return false;
	}

	priv_state priv = set_root_priv();
	virDomainPtr dom = virDomainLookupByName(m_libvirt_connection, m_vm_name.Value());
	set_priv(priv);
	if(dom == NULL)
	  {
	    virErrorPtr err = virConnGetLastError(m_libvirt_connection);
	    vmprintf(D_ALWAYS, "Error finding domain %s: %s\n", m_vm_name.Value(), (err ? err->message : "No reason found"));
	    return false;
	  }

	int result = virDomainSuspend(dom);
	virDomainFree(dom);
	if( result == 0 ) {
		// pause succeeds.
		m_is_soft_suspended = true;
		return true;
	}

	// Failed to suspend a VM softly.
	// Instead of soft suspend, we use hard suspend.
	vmprintf(D_ALWAYS, "SoftSuspend failed, so try hard Suspend instead!.\n");
	return Suspend();
}

bool
VirshType::Suspend()
{
	vmprintf(D_FULLDEBUG, "Inside VirshType::Suspend\n");

	if( (m_configfile.Length() == 0) ) {
		m_result_msg = VMGAHP_ERR_INTERNAL;
		return false;
	}

	if( getVMStatus() == VM_SUSPENDED ) {
		return true;
	}

	if( getVMStatus() != VM_RUNNING ) {
		m_result_msg = VMGAHP_ERR_VM_INVALID_OPERATION;
		return false;
	}

	if( m_xen_hw_vt && !m_allow_hw_vt_suspend ) {
		// This VM uses hardware virtualization.
		// However, Virsh cannot suspend this type of VM yet.
		m_result_msg = VMGAHP_ERR_VM_NO_SUPPORT_SUSPEND;
		return false;
	}

	// If a VM is soft suspended, resume it first.
	ResumeFromSoftSuspend();

	MyString tmpfilename;
	makeNameofSuspendfile(tmpfilename);
	unlink(tmpfilename.Value());

	priv_state priv = set_root_priv();
	virDomainPtr dom = virDomainLookupByName(m_libvirt_connection, m_vm_name.Value());
	set_priv(priv);
	if(dom == NULL)
	  {
	    virErrorPtr err = virConnGetLastError(m_libvirt_connection);
	    vmprintf(D_ALWAYS, "Error finding domain %s: %s\n", m_vm_name.Value(), (err ? err->message : "No reason found"));
	    return false;
	  }

	priv = set_root_priv();
	int result = virDomainSave(dom, tmpfilename.Value());
	virDomainFree(dom);
	set_priv(priv);
	if( result != 0 ) {
		// Read error output
// 		char *temp = cmd_out.print_to_delimed_string("/");
// 		m_result_msg = temp;
// 		free( temp );
		unlink(tmpfilename.Value());
		return false;
	}

	m_suspendfile = tmpfilename;

	setVMStatus(VM_SUSPENDED);
	m_cputime_before_suspend += m_cpu_time;
	m_cpu_time = 0;
	return true;
}

bool
VirshType::Resume()
{
	vmprintf(D_FULLDEBUG, "Inside VirshType::Resume\n");

	if((m_configfile.Length() == 0)) {
		m_result_msg = VMGAHP_ERR_INTERNAL;
		return false;
	}

	// If a VM is soft suspended, resume it first.
	ResumeFromSoftSuspend();

	if( getVMStatus() == VM_RUNNING ) {
		return true;
	}

	if( !m_restart_with_ckpt &&
			( getVMStatus() != VM_SUSPENDED) ) {
		m_result_msg = VMGAHP_ERR_VM_INVALID_OPERATION;
		return false;
	}
	m_restart_with_ckpt = false;

	m_is_checkpointed = false;

	if( check_vm_read_access_file(m_suspendfile.Value(), true) == false ) {
		m_result_msg = VMGAHP_ERR_VM_INVALID_SUSPEND_FILE;
		return false;
	}

	priv_state priv = set_root_priv();
	int result = virDomainRestore(m_libvirt_connection, m_suspendfile.Value());
	set_priv(priv);

	if( result != 0 ) {
		// Read error output
// 		char *temp = cmd_out.print_to_delimed_string("/");
// 		m_result_msg = temp;
// 		free( temp );
		return false;
	}

	setVMStatus(VM_RUNNING);
	m_cpu_time = 0;

	// Delete suspend file
	unlink(m_suspendfile.Value());
	m_suspendfile = "";
	return true;
}

bool
VirshType::Status()
{
	vmprintf(D_FULLDEBUG, "Inside VirshType::Status\n");

 	if((m_configfile.Length() == 0)) {
 		m_result_msg = VMGAHP_ERR_INTERNAL;
 		return false;
 	}

 	m_result_msg = "";

 	if( m_vm_networking ) {
 		if( m_vm_mac.IsEmpty() == false ) {
 			if( m_result_msg.IsEmpty() == false ) {
 				m_result_msg += " ";
 			}
 			m_result_msg += VMGAHP_STATUS_COMMAND_MAC;
 			m_result_msg += "=";
 			m_result_msg += m_vm_mac;
 		}

 		if( m_vm_ip.IsEmpty() == false ) {
 			if( m_result_msg.IsEmpty() == false ) {
 				m_result_msg += " ";
 			}
 			m_result_msg += VMGAHP_STATUS_COMMAND_IP;
 			m_result_msg += "=";
 			m_result_msg += m_vm_ip;
 		}
 	}

 	if( m_result_msg.IsEmpty() == false ) {
 		m_result_msg += " ";
 	}

 	m_result_msg += VMGAHP_STATUS_COMMAND_STATUS;
 	m_result_msg += "=";

	priv_state priv = set_root_priv();
	virDomainPtr dom = virDomainLookupByName(m_libvirt_connection, m_vm_name.Value());
	set_priv(priv);
	if(dom == NULL)
	{
		virErrorPtr err = virConnGetLastError(m_libvirt_connection);

	    if ( err )
		{
			switch (err->code)
			{
				case (VIR_ERR_NO_DOMAIN):
					// The VM isn't there anymore, so signal shutdown
					vmprintf(D_FULLDEBUG, "Couldn't find domain %s, assuming it was shutdown\n", m_vm_name.Value());
					if(getVMStatus() == VM_RUNNING) {
						m_self_shutdown = true;
					}
					if(getVMStatus() != VM_STOPPED) {
						setVMStatus(VM_STOPPED);
						m_stop_time.getTime();
					}
					m_result_msg += "Stopped";
					return true;
				break;
				case (VIR_ERR_SYSTEM_ERROR): //
					vmprintf(D_ALWAYS, "libvirt communication error detected, attempting to reconnect...\n");
					this->Connect();
					if ( NULL == ( dom = virDomainLookupByName(m_libvirt_connection, m_vm_name.Value() ) ) )
					{
						vmprintf(D_ALWAYS, "could not reconnect to libvirt... marking vm as stopped (should exit)\n");
						if(getVMStatus() == VM_RUNNING) {
							m_self_shutdown = true;
						}
						if(getVMStatus() != VM_STOPPED) {
							setVMStatus(VM_STOPPED);
							m_stop_time.getTime();
						}
						m_result_msg += "Stopped";
						return true;
					}
					else
					{
						vmprintf(D_ALWAYS, "libvirt has successfully reconnected!\n");
					}
				break;
			default:
				vmprintf(D_ALWAYS, "Error finding domain %s(%d): %s\n", m_vm_name.Value(), err->code, err->message );
				return false;
			}
		}
		else
		{
			vmprintf(D_ALWAYS, "Error finding domain, no error returned from libvirt\n" );
			return false;
		}

	}

	virDomainInfo _info;
	virDomainInfoPtr info = &_info;
	if(virDomainGetInfo(dom, info) < 0)
	  {
	    virErrorPtr err = virConnGetLastError(m_libvirt_connection);
	    vmprintf(D_ALWAYS, "Error finding domain info %s: %s\n", m_vm_name.Value(), (err ? err->message : "No reason found"));
	    return false;
	  }
	if(info->state == VIR_DOMAIN_RUNNING || info->state == VIR_DOMAIN_BLOCKED)
	  {
	    static unsigned long long LastCpuTime = 0;
	    static time_t LastStamp = time(0);

	    time_t CurrentStamp = time(0);
	    unsigned long long CurrentCpuTime= info->cpuTime;
	    double percentUtilization=1.0;

	    setVMStatus(VM_RUNNING);
	    // libvirt reports cputime in nanoseconds
	    m_cpu_time = info->cpuTime / 1000000000.0;
	    m_result_msg += "Running";

	    // Here is where we tack on utilization we only cate about the
	    m_result_msg += " ";
	    m_result_msg += VMGAHP_STATUS_COMMAND_CPUUTILIZATION;
	    m_result_msg += "=";

	    if ( (CurrentStamp - LastStamp) > 0 )
	    {
	      // Old calc method because of libvirt version mismatches.
	      // courtesy of http://people.redhat.com/~rjones/virt-top/faq.html#calccpu
	      percentUtilization = (1.0 * (CurrentCpuTime-LastCpuTime) ) / ((CurrentStamp - LastStamp)*info->nrVirtCpu*1000000000.0);
	      vmprintf(D_FULLDEBUG, "Computing utilization %f = (%llu) / (%d * %d * 1000000000.0)\n",percentUtilization, (CurrentCpuTime-LastCpuTime), (int) (CurrentStamp - LastStamp), info->nrVirtCpu );
	    }

	    m_result_msg += percentUtilization;

	    LastCpuTime = CurrentCpuTime;
	    LastStamp = CurrentStamp;

	    virDomainFree(dom);
	    return true;
	  }
	else if(info->state == VIR_DOMAIN_PAUSED)
	  {
	    m_result_msg += "Suspended";
	    virDomainFree(dom);
	    return true;
	  }
	else
	  {
	    if(getVMStatus() == VM_RUNNING)
	      {
		m_self_shutdown = true;
	      }
	    if(getVMStatus() != VM_STOPPED)
	      {
		setVMStatus(VM_STOPPED);
		m_stop_time.getTime();
	      }
	    m_result_msg += "Stopped";
	    virDomainFree(dom);
	    return true;
	  }
	virDomainFree(dom);
	return false;
}

/*
 * Just so that we can get out of the habit of using "goto".
 */
void virshIOError(const char * filename, FILE * fp)
{
	vmprintf(D_ALWAYS, "failed to fprintf in CreateVirshConfigFile(%s:%s)\n",
			filename, strerror(errno));
	if( fp ) {
		fclose(fp);
	}
	unlink(filename);

}

bool KVMType::CreateVirshConfigFile(const char * filename)
{
	MyString disk_string;

	if(!filename) return false;

  // The old way of doing things was to write the XML directly to a
  // file; the new way is to store it in m_xml.

	if (!VirshType::CreateVirshConfigFile(filename))
	{
		vmprintf(D_ALWAYS, "KVMType::CreateVirshConfigFile no XML found, generating defaults\n");

		m_xml += "<domain type='kvm'>";
		m_xml += "<name>";
		m_xml += m_vm_name;
		m_xml += "</name>";
		m_xml += "<memory>";
		m_xml += m_vm_mem * 1024;
		m_xml += "</memory>";
		m_xml += "<vcpu>";
		m_xml += m_vcpus;
		m_xml += "</vcpu>";
		m_xml += "<os><type>hvm</type></os>";
		m_xml += "<devices>";
		m_xml += "<console type='pty'><source path='/dev/ptmx'/></console>";
		if( m_vm_networking )
			{
			vmprintf(D_FULLDEBUG, "mac address is %s\n", m_vm_job_mac.Value());
			if( m_vm_networking_type.find("nat") >= 0 ) {
			m_xml += "<interface type='network'><source network='default'/></interface>";
			}
			else if( m_vm_networking_type.find("bridge") >= 0 )
			{
			m_xml += "<interface type='bridge'>";
			if (!m_vm_bridge_interface.empty()) {
				m_xml += "<source bridge='";
				m_xml += m_vm_bridge_interface.c_str();
				m_xml += "'/>";
			}
			if(!m_vm_job_mac.IsEmpty())
				{
				m_xml += "<mac address='";
				m_xml += m_vm_job_mac;
				m_xml += "'/>";
				}
			m_xml += "</interface>";
			}
			}
		disk_string = makeVirshDiskString();

		m_xml += disk_string;
		m_xml += "</devices></domain>";

	}

	return true;
}

bool
XenType::CreateVirshConfigFile(const char* filename)
{
	MyString disk_string;

	if( !filename ) return false;

	if (!VirshType::CreateVirshConfigFile(filename))
	{
		vmprintf(D_ALWAYS, "XenType::CreateVirshConfigFile no XML found, generating defaults\n");

		m_xml += "<domain type='xen'>";
		m_xml += "<name>";
		m_xml += m_vm_name;
		m_xml += "</name>";
		m_xml += "<memory>";
		m_xml += m_vm_mem * 1024;
		m_xml += "</memory>";
		m_xml += "<vcpu>";
		m_xml += m_vcpus;
		m_xml += "</vcpu>";
		m_xml += "<os><type>linux</type>";

		if( m_xen_kernel_file.IsEmpty() == false ) {
			m_xml += "<kernel>";
			m_xml += m_xen_kernel_file;
			m_xml += "</kernel>";
			if( m_xen_initrd_file.IsEmpty() == false ) {
				m_xml += "<initrd>";
				m_xml += m_xen_initrd_file;
				m_xml += "</initrd>";
			}
			if( m_xen_root.IsEmpty() == false ) {
				m_xml += "<root>";
				m_xml += m_xen_root;
				m_xml += "</root>";
			}

			if( m_xen_kernel_params.IsEmpty() == false ) {
				m_xml += "<cmdline>";
				m_xml += m_xen_kernel_params;
				m_xml += "</cmdline>";
			}
		}

		m_xml += "</os>";
		if( strcasecmp(m_xen_kernel_submit_param.Value(), XEN_KERNEL_INCLUDED) == 0) {
			m_xml += "<bootloader>";
			m_xml += m_xen_bootloader;
			m_xml += "</bootloader>";
		}
		m_xml += "<devices>";
		m_xml += "<console type='pty'><source path='/dev/ptmx'/></console>";
		if( m_vm_networking ) {
			if( m_vm_networking_type.find("nat") >= 0 ) {
				m_xml += "<interface type='network'><source network='default'/></interface>";
			} else if( m_vm_networking_type.find("bridge") >= 0 ){
				m_xml += "<interface type='bridge'>";
				if (!m_vm_bridge_interface.empty()) {
					m_xml += "<source bridge='";
					m_xml += m_vm_bridge_interface.c_str();
					m_xml += "'/>";
				}
				vmprintf(D_FULLDEBUG, "mac address is %s", m_vm_job_mac.Value());
				if(!m_vm_job_mac.IsEmpty())
				{
					m_xml += "<mac address='";
					m_xml += m_vm_job_mac;
					m_xml += "'/>";
				}
				m_xml += "</interface>";
			}
		}

		// Create disk parameter in Virsh config file
		disk_string = makeVirshDiskString();
		m_xml += disk_string;
		m_xml += "</devices></domain>";
	}

	return true;
}

bool
VirshType::CreateXenVMConfigFile(const char* filename)
{
	if( !filename ) {
		return false;
	}

       	return CreateVirshConfigFile(filename);
}

void VirshType::Connect()
{
	priv_state priv = set_root_priv();

	if ( m_libvirt_connection )
	{
		virConnectClose( m_libvirt_connection );
	}

    m_libvirt_connection = virConnectOpen( m_sessionID.c_str() );
    set_priv(priv);

  	if( m_libvirt_connection == NULL )
    {
		virErrorPtr err = virGetLastError();
		EXCEPT("Failed to create libvirt connection: %s", (err ? err->message : "No reason found"));
    }
}

bool
VirshType::parseXenDiskParam(const char *format)
{
	if( !format || (format[0] == '\0') ) {
		return false;
	}

    vmprintf(D_FULLDEBUG, "format = %s\n", format);

	StringList working_files;
	find_all_files_in_dir(m_workingpath.Value(), working_files, true);

	StringList disk_files(format, ",");
	if( disk_files.isEmpty() ) {
		return false;
	}

	disk_files.rewind();
	const char *one_disk = NULL;
	while( (one_disk = disk_files.next() ) != NULL ) {
		// found a disk file
		StringList single_disk_file(one_disk, ":");
        int iNumParams = single_disk_file.number();
		if( iNumParams < 3 || iNumParams > 4 )
        {
			return false;
		}

		single_disk_file.rewind();

		// name of a disk file
		MyString dfile = single_disk_file.next();
		if( dfile.IsEmpty() ) {
			return false;
		}
		dfile.trim();

		const char* tmp_base_name = condor_basename(dfile.Value());
		if( !tmp_base_name ) {
			return false;
		}

		// Every disk file for Virsh must have full path name
		MyString disk_file;
		if( filelist_contains_file(dfile.Value(),
					&working_files, true) ) {
			// file is transferred
			disk_file = m_workingpath;
			disk_file += DIR_DELIM_CHAR;
			disk_file += tmp_base_name;

			m_has_transferred_disk_file = true;
		}else {
			// file is not transferred.
			if( fullpath(dfile.Value()) == false) {
				vmprintf(D_ALWAYS, "File(%s) for xen disk "
						"should have full path name\n", dfile.Value());
				return false;
			}
			disk_file = dfile;
		}

		// device name
		MyString disk_device = single_disk_file.next();
		disk_device.trim();
		disk_device.lower_case();

		// disk permission
		MyString disk_perm = single_disk_file.next();
		disk_perm.trim();

		if( !strcasecmp(disk_perm.Value(), "w") || !strcasecmp(disk_perm.Value(), "rw"))
        {
			// check if this disk file is writable
			if( check_vm_write_access_file(disk_file.Value(), false) == false ) {
				vmprintf(D_ALWAYS, "xen disk image file('%s') cannot be modified\n",
					   	disk_file.Value());
				return false;
			}
		}else
        {
			// check if this disk file is readable
			if( check_vm_read_access_file(disk_file.Value(), false) == false ) {
				vmprintf(D_ALWAYS, "xen disk image file('%s') cannot be read\n",
						disk_file.Value());
				return false;
			}
		}

		XenDisk *newdisk = new XenDisk;
		ASSERT(newdisk);
		newdisk->filename = disk_file;
		newdisk->device = disk_device;
		newdisk->permission = disk_perm;

        // only when a format is specified do we check
        if (iNumParams == 4 )
        {
            newdisk->format = single_disk_file.next();
            newdisk->format.trim();
            newdisk->format.lower_case();
        }

		m_disk_list.Append(newdisk);
	}

	if( m_disk_list.Number() == 0 ) {
		vmprintf(D_ALWAYS, "No valid Virsh disk\n");
		return false;
	}

	return true;
}

// This function should be called after parseVirshDiskParam
MyString
VirshType::makeVirshDiskString(void)
{
	MyString xendisk;
	xendisk = "";
	bool first_disk = true;

	XenDisk *vdisk = NULL;
	m_disk_list.Rewind();
	while( m_disk_list.Next(vdisk) ) {
		if( !first_disk ) {
			xendisk += "</disk>";
		}
		first_disk = false;
        if ( strstr (vdisk->filename.Value(), ".iso") )
        {
            xendisk += "<disk type='file' device='cdrom'>";
        }
        else
        {
            xendisk += "<disk type='file'>";
        }

        if (vdisk->format.Length())
        {
           xendisk += "<driver name='qemu' type='";
           xendisk += vdisk->format;
           xendisk += "'/>";
        }

		xendisk += "<source file='";
		xendisk += vdisk->filename;
		xendisk += "'/>";
		xendisk += "<target dev='";
		xendisk += vdisk->device;
		xendisk += "'/>";

		if (vdisk->permission == "r") {
			xendisk += "<readonly/>";
		}
	}
	xendisk += "</disk>";

	return xendisk;
}

bool
VirshType::writableXenDisk(const char* file)
{
	if( !file ) {
		return false;
	}

	XenDisk *vdisk = NULL;
	m_disk_list.Rewind();
	while( m_disk_list.Next(vdisk) ) {
		if( !strcasecmp(basename(file), basename(vdisk->filename.Value())) ) {
			if( !strcasecmp(vdisk->permission.Value(), "w") ||
					!strcasecmp(vdisk->permission.Value(), "rw")) {
				return true;
			}else {
				return false;
			}
		}
	}
	return false;
}

void
VirshType::updateLocalWriteDiskTimestamp(time_t timestamp)
{
	char *tmp_file = NULL;
	StringList working_files;
	struct utimbuf timewrap;

	find_all_files_in_dir(m_workingpath.Value(), working_files, true);

	working_files.rewind();
	while( (tmp_file = working_files.next()) != NULL ) {
		// In Virsh, disk file is generally used via loopback-mounted
		// file. However, mtime of those files is not updated
		// even after modification.
		// So we manually update mtimes of writable disk files.
		if( writableXenDisk(tmp_file) ) {
			timewrap.actime = timestamp;
			timewrap.modtime = timestamp;
			utime(tmp_file, &timewrap);
		}
	}
}

bool
VirshType::createCkptFiles(void)
{
	vmprintf(D_FULLDEBUG, "Inside VirshType::createCkptFiles\n");

	// This function will suspend a running VM.
	if( getVMStatus() == VM_STOPPED ) {
		vmprintf(D_ALWAYS, "createCkptFiles is called for a stopped VM\n");
		return false;
	}

	if( getVMStatus() == VM_RUNNING ) {
		if( Suspend() == false ) {
			return false;
		}
	}

	if( getVMStatus() == VM_SUSPENDED ) {
		// we create timestampfile
		time_t current_time = time(NULL);
		MyString timestampfile(m_suspendfile);
		timestampfile += XEN_CKPT_TIMESTAMP_FILE_SUFFIX;

		FILE *fp = safe_fopen_wrapper(timestampfile.Value(), "w");
		if( !fp ) {
			vmprintf(D_ALWAYS, "failed to safe_fopen_wrapper file(%s) for "
					"checkpoint timestamp : safe_fopen_wrapper returns %s\n",
					timestampfile.Value(), strerror(errno));
			Resume();
			return false;
		}
		if( fprintf(fp, "%d\n", (int)current_time) < 0 ) {
			fclose(fp);
			unlink(timestampfile.Value());
			vmprintf(D_ALWAYS, "failed to fprintf for checkpoint timestamp "
					"file(%s:%s)\n", timestampfile.Value(), strerror(errno));
			Resume();
			return false;
		}
		fclose(fp);

		// checkpoint succeeds
		m_is_checkpointed = true;
		return true;
	}

	return false;
}

bool KVMType::checkXenParams(VMGahpConfig * config)
{
  MyString fixedvalue;
  if( !config ) {
    return false;
  }

  // Do we need to check for both read and write access?
  if(check_vm_read_access_file("/dev/kvm", true) == false) {
    vmprintf(D_ALWAYS, "\nFile Permission Error: Cannot read /dev/kvm as root\n");
    return false;
  }
  if(check_vm_write_access_file("/dev/kvm", true) == false) {
    vmprintf(D_ALWAYS, "\nFile Permission Error: Cannot write /dev/kvm as root\n");
    return false;
  }
  return true;

}

bool
KVMType::killVMFast(const char* vmname)
{
	vmprintf(D_FULLDEBUG, "Inside KVMType::killVMFast\n");
	priv_state priv = set_root_priv();
	virConnectPtr libvirt_connection = virConnectOpen("qemu:///session");
	set_priv(priv);
	return VirshType::killVMFast(vmname, libvirt_connection);
}

bool
XenType::checkXenParams(VMGahpConfig* config)
{
	char *config_value = NULL;
	MyString fixedvalue;
	vmprintf(D_FULLDEBUG, "In XenType::checkXenParams()\n");
	if( !config ) {
		return false;
	}

	// Read XEN_BOOTLOADER (required parameter)
	config_value = param("XEN_BOOTLOADER");
	if( !config_value ) {
		vmprintf(D_ALWAYS, "\nERROR: You should define Xen bootloader\n");
		return false;
	}
	fixedvalue = delete_quotation_marks(config_value);
	free(config_value);

	// Can read xen boot loader ?
	if( check_vm_read_access_file(fixedvalue.Value(), true) == false ) {
		vmprintf(D_ALWAYS, "Xen bootloader file '%s' cannot be read\n",
				fixedvalue.Value());
		return false;
	}
	return true;
}

bool
XenType::killVMFast(const char* vmname)
{
	vmprintf(D_FULLDEBUG, "Inside XenType::killVMFast\n");
	priv_state priv = set_root_priv();
	virConnectPtr libvirt_connection = virConnectOpen("xen:///");
	set_priv(priv);
	return VirshType::killVMFast(vmname, libvirt_connection);
}

void
VirshType::makeNameofSuspendfile(MyString& name)
{
	name.sprintf("%s%c%s", m_workingpath.Value(), DIR_DELIM_CHAR,
			XEN_MEM_SAVED_FILE);
}

// This function compares the timestamp of given file with
// that of writable disk files.
// This function should be called after parseVirshDiskParam()
bool
VirshType::checkCkptSuspendFile(const char* file)
{
	if( !file || file[0] == '\0' ) {
		return false;
	}

	// Read timestamp file
	MyString timestampfile(file);
	timestampfile += XEN_CKPT_TIMESTAMP_FILE_SUFFIX;

	FILE *fp = safe_fopen_wrapper(timestampfile.Value(), "r");
	if( !fp ) {
		vmprintf(D_ALWAYS, "Cannot find the timestamp file\n");
		return false;
	}

	char buffer[128];
	if( fgets(buffer, sizeof(buffer), fp) == NULL ) {
		fclose(fp);
		vmprintf(D_ALWAYS, "Invalid timestamp file\n");
		return false;
	}
	fclose(fp);

	MyString tmp_str;
	tmp_str = buffer;
	if( tmp_str.IsEmpty() ) {
		vmprintf(D_ALWAYS, "Invalid timestamp file\n");
		return false;
	}
	tmp_str.trim();

	time_t timestamp;
	timestamp = (time_t)atoi(tmp_str.Value());
	if( timestamp <= 0 ) {
		vmprintf(D_ALWAYS, "Invalid timestamp file\n");
		return false;
	}

	XenDisk *vdisk = NULL;
	m_disk_list.Rewind();
	while( m_disk_list.Next(vdisk) ) {
		if( !strcasecmp(vdisk->permission.Value(), "w") ||
				!strcasecmp(vdisk->permission.Value(), "rw")) {
			// this is a writable disk file
			// get modification time of this disk file
			time_t disk_mtime;
			struct stat disk_stat;
			if( stat(vdisk->filename.Value(), &disk_stat ) < 0 ) {
				vmprintf(D_ALWAYS, "\nERROR: Failed to access \"%s\":%s\n",
						vdisk->filename.Value(), strerror(errno));
				return false;
			}
			disk_mtime = disk_stat.st_mtime;

			// compare
			if( disk_mtime != timestamp ) {
				vmprintf(D_ALWAYS, "Checkpoint timestamp mismatch: "
						"timestamp of suspend file=%d, mtime of disk file=%d\n",
						(int)timestamp, (int)disk_mtime);
				return false;
			}
		}
	}

	return true;
}

bool
VirshType::findCkptConfigAndSuspendFile(MyString &vmconfig, MyString &suspendfile)
{
	if( m_transfer_intermediate_files.isEmpty() ) {
		return false;
	}

	vmconfig = "";
	suspendfile = "";

	MyString tmpconfig;
	MyString tmpsuspendfile;

	if( filelist_contains_file( XEN_CONFIG_FILE_NAME,
				&m_transfer_intermediate_files, true) ) {
		// There is a vm config file for checkpointed files
		tmpconfig.sprintf("%s%c%s",m_workingpath.Value(),
				DIR_DELIM_CHAR, XEN_CONFIG_FILE_NAME);
	}

	MyString tmp_name;
	makeNameofSuspendfile(tmp_name);

	if( filelist_contains_file(tmp_name.Value(),
				&m_transfer_intermediate_files, true) ) {
		// There is a suspend file that was created during vacate
		tmpsuspendfile = tmp_name;
		if( check_vm_read_access_file(tmpsuspendfile.Value(), true) == false) {
			return false;
		}
	}

	if( (tmpconfig.Length() > 0) &&
			(tmpsuspendfile.Length() > 0 )) {
		// check if the timestamp of suspend file is same to
		// that of writable disk files
		// If timestamp differs between suspend file and writable disk file,
		// it means that there was a crash.
		// So we need to restart this VM job from the beginning.
		if( checkCkptSuspendFile(tmpsuspendfile.Value()) ) {
			vmconfig = tmpconfig;
			suspendfile = tmpsuspendfile;
			return true;
		}
	}
	return false;
}

bool
VirshType::killVM()
{
	vmprintf(D_FULLDEBUG, "Inside VirshType::killVM\n");

	if( ( m_vm_name.Length() == 0 ) ) {
		return false;
	}

	// If a VM is soft suspended, resume it first.
	ResumeFromSoftSuspend();

	return killVMFast(m_vm_name.Value(), m_libvirt_connection);
}

bool
VirshType::killVMFast(const char* vmname, virConnectPtr libvirt_con)
{
	vmprintf(D_FULLDEBUG, "Inside VirshType::killVMFast\n");

	if( !vmname || (vmname[0] == '\0') ) {
		return false;
	}

	priv_state priv = set_root_priv();
	virDomainPtr dom = virDomainLookupByName(libvirt_con, vmname);
	set_priv(priv);
	if(dom == NULL)
	  {
	    virErrorPtr err = virConnGetLastError(libvirt_con);
	    if (err && err->code != VIR_ERR_NO_DOMAIN)
	      {
		vmprintf(D_ALWAYS, "Error finding domain %s: %s\n", vmname, (err ? err->message : "No reason found"));
		return false;
	      }
	    else
	      {
		return true;
	      }
	  }

	priv = set_root_priv();
	bool ret = (virDomainDestroy(dom) == 0);
	virDomainFree(dom);
	set_priv(priv);
	return ret;
}

XenType::XenType(const char * workingpath, ClassAd * ad)
  : VirshType(workingpath, ad)
{

  m_sessionID="xen:///";
  this->Connect();
  m_vmtype = CONDOR_VM_UNIVERSE_XEN;

}

bool XenType::CreateConfigFile()
{
	char *config_value = NULL;
	priv_state priv;

	vmprintf(D_FULLDEBUG, "In XenType::CreateConfigFile()\n");
	// Read common parameters for VM
	// and create the name of this VM
	if( parseCommonParamFromClassAd(true) == false ) {
		return false;
	}

	if( m_vm_mem < 32 ) {
		// Allocating less than 32MBs is not recommended in Virsh.
		m_vm_mem = 32;
	}

	// Read the parameter of Virsh kernel
	if( m_classAd.LookupString( VMPARAM_XEN_KERNEL, m_xen_kernel_submit_param) != 1 ) {
		vmprintf(D_ALWAYS, "%s cannot be found in job classAd\n",
							VMPARAM_XEN_KERNEL);
		m_result_msg = VMGAHP_ERR_JOBCLASSAD_XEN_NO_KERNEL_PARAM;
		return false;
	}
	m_xen_kernel_submit_param.trim();

	if(strcasecmp(m_xen_kernel_submit_param.Value(), XEN_KERNEL_INCLUDED) == 0 )
	{
		//if (strcasecmp(vm_type.Value(), CONDOR_VM_UNIVERSE_XEN) == 0){
			vmprintf(D_ALWAYS, "VMGahp will use xen bootloader\n");
			config_value = param( "XEN_BOOTLOADER" );
			if( !config_value ) {
				vmprintf(D_ALWAYS, "xen bootloader is not defined "
						"in vmgahp config file\n");
				m_result_msg = VMGAHP_ERR_CRITICAL;
				return false;
			}else {
				m_xen_bootloader = delete_quotation_marks(config_value);
				free(config_value);
			}
		//}
	}else if(strcasecmp(m_xen_kernel_submit_param.Value(), XEN_KERNEL_HW_VT) == 0) {
		vmprintf(D_ALWAYS, "This VM requires hardware virtualization\n");
		if( !m_vm_hardware_vt ) {
			m_result_msg = VMGAHP_ERR_JOBCLASSAD_MISMATCHED_HARDWARE_VT;
			return false;
		}
		m_xen_hw_vt = true;
		m_allow_hw_vt_suspend =
			param_boolean("XEN_ALLOW_HARDWARE_VT_SUSPEND", false);
	}else {
		// A job user defined a customized kernel
		// make sure that the file for xen kernel is readable
		if( check_vm_read_access_file(m_xen_kernel_submit_param.Value(), false) == false) {
			vmprintf(D_ALWAYS, "xen kernel file '%s' cannot be read\n",
					m_xen_kernel_submit_param.Value());
			m_result_msg = VMGAHP_ERR_JOBCLASSAD_XEN_KERNEL_NOT_FOUND;
			return false;
		}
		MyString tmp_fullname;
		if( isTransferedFile(m_xen_kernel_submit_param.Value(),
					tmp_fullname) ) {
			// this file is transferred
			// we need a full path
			m_xen_kernel_submit_param = tmp_fullname;
		}

		m_xen_kernel_file = m_xen_kernel_submit_param;

		if( m_classAd.LookupString(VMPARAM_XEN_INITRD, m_xen_initrd_file)
					== 1 ) {
			// A job user defined a customized ramdisk
			m_xen_initrd_file.trim();
			if( check_vm_read_access_file(m_xen_initrd_file.Value(), false) == false) {
				// make sure that the file for xen ramdisk is readable
				vmprintf(D_ALWAYS, "xen ramdisk file '%s' cannot be read\n",
						m_xen_initrd_file.Value());
				m_result_msg = VMGAHP_ERR_JOBCLASSAD_XEN_INITRD_NOT_FOUND;
				return false;
			}
			if( isTransferedFile(m_xen_initrd_file.Value(),
						tmp_fullname) ) {
				// this file is transferred
				// we need a full path
				m_xen_initrd_file = tmp_fullname;
			}
		}
	}

	if( m_xen_kernel_file.IsEmpty() == false ) {
		// Read the parameter of Virsh Root
		if( m_classAd.LookupString(VMPARAM_XEN_ROOT, m_xen_root) != 1 ) {
			vmprintf(D_ALWAYS, "%s cannot be found in job classAd\n",
					VMPARAM_XEN_ROOT);
			m_result_msg = VMGAHP_ERR_JOBCLASSAD_XEN_NO_ROOT_DEVICE_PARAM;
			return false;
		}
		m_xen_root.trim();
	}

	MyString xen_disk;
	// Read the parameter of Virsh Disk
	if( m_classAd.LookupString(VMPARAM_VM_DISK, xen_disk) != 1 ) {
		vmprintf(D_ALWAYS, "%s cannot be found in job classAd\n",
				VMPARAM_VM_DISK);
		m_result_msg = VMGAHP_ERR_JOBCLASSAD_XEN_NO_DISK_PARAM;
		return false;
	}
	xen_disk.trim();

	// from this point below we start to check the params
	// and access data.
	priv = set_root_priv();

	if( parseXenDiskParam(xen_disk.Value()) == false ) {
		vmprintf(D_ALWAYS, "xen disk format(%s) is incorrect\n",
				xen_disk.Value());
		m_result_msg = VMGAHP_ERR_JOBCLASSAD_XEN_INVALID_DISK_PARAM;
		set_priv(priv);
		return false;
	}
	set_priv(priv);

	// Read the parameter of Virsh Kernel Param
	if( m_classAd.LookupString(VMPARAM_XEN_KERNEL_PARAMS, m_xen_kernel_params) == 1 ) {
		m_xen_kernel_params.trim();
	}

	// Check whether this is re-starting after vacating checkpointing,
	if( m_transfer_intermediate_files.isEmpty() == false ) {
		// we have chckecpointed files
		// Find vm config file and suspend file
		MyString ckpt_config_file;
		MyString ckpt_suspend_file;
		if( findCkptConfigAndSuspendFile(ckpt_config_file, ckpt_suspend_file)
				== false ) {
			vmprintf(D_ALWAYS, "Checkpoint files exist but "
					"cannot find the correct config file and suspend file\n");
			//Delete all non-transferred files from submit machine
			deleteNonTransferredFiles();
			m_restart_with_ckpt = false;
		}else {
			m_configfile = ckpt_config_file;
			m_suspendfile = ckpt_suspend_file;
			vmprintf(D_ALWAYS, "Found checkpointed files, "
					"so we start using them\n");
			m_restart_with_ckpt = true;
			return true;
		}
	}

	// Here we check if this job actually can use checkpoint
	if( m_vm_checkpoint ) {
		// For vm checkpoint in Virsh
		// 1. all disk files should be in a shared file system
		// 2. If a job uses CDROM files, it should be
		// 	  single ISO file and be in a shared file system
		if( m_has_transferred_disk_file ) {
			// In this case, we cannot use vm checkpoint for Virsh
			// To use vm checkpoint in Virsh,
			// all disk and iso files should be in a shared file system
			vmprintf(D_ALWAYS, "To use vm checkpint in Virsh, "
					"all disk and iso files should be "
					"in a shared file system\n");
			m_result_msg = VMGAHP_ERR_JOBCLASSAD_XEN_MISMATCHED_CHECKPOINT;
			return false;
		}
	}

	// create a vm config file
	MyString tmp_config_name;
	tmp_config_name.sprintf("%s%c%s",m_workingpath.Value(),
			DIR_DELIM_CHAR, XEN_CONFIG_FILE_NAME);

	vmprintf(D_ALWAYS, "CreateXenVMConfigFile\n");

	if( CreateXenVMConfigFile(tmp_config_name.Value())
			== false ) {
		m_result_msg = VMGAHP_ERR_CRITICAL;
		return false;
	}

	// set vm config file
	m_configfile = tmp_config_name.Value();
	return true;
}

KVMType::KVMType(const char * workingpath, ClassAd * ad)
  : VirshType(workingpath, ad)
{

	m_sessionID="qemu:///session";
	this->Connect();
	m_vmtype = CONDOR_VM_UNIVERSE_KVM;

}

bool
KVMType::CreateConfigFile()
{
	priv_state priv;

	vmprintf(D_FULLDEBUG, "In KVMType::CreateConfigFile()\n");
	// Read common parameters for VM
	// and create the name of this VM
	if( parseCommonParamFromClassAd(true) == false ) {
		return false;
	}

	if( m_vm_mem < 32 ) {
		// Allocating less than 32MBs is not recommended in Virsh.
		m_vm_mem = 32;
	}

	MyString kvm_disk;
	// Read the parameter of Virsh Disk
	if( m_classAd.LookupString(VMPARAM_VM_DISK, kvm_disk) != 1 ) {
		vmprintf(D_ALWAYS, "%s cannot be found in job classAd\n",
				VMPARAM_VM_DISK);
		m_result_msg = VMGAHP_ERR_JOBCLASSAD_XEN_NO_DISK_PARAM;
		return false;
	}
	kvm_disk.trim();

	// from this point below we start to check the params
	// and access data.
	priv = set_root_priv();

	if( parseXenDiskParam(kvm_disk.Value()) == false ) {
		vmprintf(D_ALWAYS, "kvm disk format(%s) is incorrect\n",
				kvm_disk.Value());
		m_result_msg = VMGAHP_ERR_JOBCLASSAD_KVM_INVALID_DISK_PARAM;
		set_priv(priv);
		return false;
	}
	set_priv(priv);

	// Check whether this is re-starting after vacating checkpointing,
	if( m_transfer_intermediate_files.isEmpty() == false ) {
		// we have chckecpointed files
		// Find vm config file and suspend file
		MyString ckpt_config_file;
		MyString ckpt_suspend_file;
		if( findCkptConfigAndSuspendFile(ckpt_config_file, ckpt_suspend_file)
				== false ) {
			vmprintf(D_ALWAYS, "Checkpoint files exist but "
					"cannot find the correct config file and suspend file\n");
			//Delete all non-transferred files from submit machine
			deleteNonTransferredFiles();
			m_restart_with_ckpt = false;
		}else {
			m_configfile = ckpt_config_file;
			m_suspendfile = ckpt_suspend_file;
			vmprintf(D_ALWAYS, "Found checkpointed files, "
					"so we start using them\n");
			m_restart_with_ckpt = true;
			return true;
		}
	}

	// Here we check if this job actually can use checkpoint
	if( m_vm_checkpoint ) {
		// For vm checkpoint in Virsh
		// 1. all disk files should be in a shared file system
		if( m_has_transferred_disk_file ) {
			// In this case, we cannot use vm checkpoint for Virsh
			// To use vm checkpoint in Virsh,
			// all disk and iso files should be in a shared file system
			vmprintf(D_ALWAYS, "To use vm checkpint in Virsh, "
					"all disk and iso files should be "
					"in a shared file system\n");
			m_result_msg = VMGAHP_ERR_JOBCLASSAD_KVM_MISMATCHED_CHECKPOINT;
			return false;
		}
	}

	// create a vm config file
	MyString tmp_config_name;
	tmp_config_name.sprintf("%s%c%s",m_workingpath.Value(),
			DIR_DELIM_CHAR, XEN_CONFIG_FILE_NAME);

	vmprintf(D_ALWAYS, "CreateKvmVMConfigFile\n");

	if( CreateXenVMConfigFile(tmp_config_name.Value())
			== false ) {
		m_result_msg = VMGAHP_ERR_CRITICAL;
		return false;
	}

	// set vm config file
	m_configfile = tmp_config_name.Value();
	return true;
}

#endif

redpajama_set_name: RedPajamaGithub


text:
import {BaseClassMethodParameterStructure} from "./base";

export interface ClassMethodParameterStructure extends BaseClassMethodParameterStructure {
}


text:
Contributions are **welcome** and will be fully **credited**.

I accept contributions via Pull Requests on [Github](https://github.com/c4pone/pagespeed).

## Running Tests

``` bash
$ composer test
```

**Happy coding**!


text:
/*====================================================
	- HTML Table Filter Generator stylesheet
	- do not hesitate to edit classes below to
	change filter grid appearance
=====================================================*/

/* TABLE LAYOUT
=====================================================*/
table.TF{
	font:normal 12px arial, tahoma, helvetica, sans-serif;
	border-top:1px solid #D0D0D0; border-left:1px solid #D0D0D0;
	border-bottom:1px solid #ccc; border-right:1px solid #ccc;
}
table.TF th{
	background-color:#EBECEE;
	border-bottom:1px solid #D0D0D0; border-right:1px solid #D0D0D0;
	border-left:1px solid #fff; border-top:1px solid #fff;
	padding:5px 5px 5px 5px; color:#333;
}

table.TF td{ padding:5px 5px 5px 5px; border-bottom:1px solid #D0D0D0; border-right:1px solid #D0D0D0; }

/* FILTERS BAR
=====================================================*/
.fltrow{ /* filter grid row appearance */
	height:20px;
	background-color:#f4f4f4;
}
.fltrow td, .fltrow th{ padding:2px !important; }
.btnflt{ /* button appearance */
	font-size:11px; vertical-align:middle;
	margin:0 2px 0 2px; padding:0 1px 0 1px;
}
.btnflt_icon{ /* button appearance when displayIcons is true */
	width:19px; height:19px; cursor:pointer !important;
	border:0 !important; vertical-align:middle;
	background:transparent url(TF_Themes/btn_filter.png) center center no-repeat !important;
}
.flt{ /* filter (input) appearance */
	background-color:#fff; font-size:10px;
	border:1px solid #ccc;
	margin:0; width:97%; vertical-align:middle;
}
select.flt{ /* select filter */
	background-color:#fff; border:1px solid #ccc;
	margin:0 1px 1px 0; width:99%; font-size:9px; vertical-align:middle;
}
select.flt_multi{ /* multiple select filter */
	background-color:#fff; border:1px solid #ccc;
	margin:0 1px 1px 0; width:99%; height:100px;
	font-size:9px; vertical-align:middle;
}
.flt_s{ /* small filter (input) appearance */
	background-color:#fff; font-size:10px;
	border:1px solid #ccc;
	margin:0; width:80%; vertical-align:middle;
}
.single_flt{ /* single filter appearance (input) */
	background-color:#fff; font-size:11px;
	border-left:1px solid #999; border-right:1px solid #ccc;
	border-top:1px solid #ccc; border-bottom:1px solid #999;
	margin:0; width:120px; vertical-align:middle;
}
.fltWatermark{ /* watermark input */
	color:#999;
}
.div_checklist{ /* div containing checklist  */
	width:100%; height:100px;
	border:1px solid #ccc;
	overflow:auto; text-align:left;
	background-color:#fff;
	color:#333 !important;
}

.div_checklist ul.flt_checklist{ padding:0 !important; margin:0 !important; list-style: none !important; }
.div_checklist li.flt_checklist_item{ /*check list item*/
	padding:1px !important; margin:0 !important; font-size:10px !important;
	border-bottom:1px solid #ccc !important;
}
.div_checklist li.flt_checklist_item:hover{
	background-color:#335EA8 !important;
	color:#fff !important;
}
.flt_checklist_item_disabled{ background-color:#E5E5E5; }
.flt_checklist_slc_item{ /*selected check list item*/
	background-color:#335EA8 !important;
	color:#fff !important;
}
.div_checklist label{ display:block !important; }
.div_checklist input{ vertical-align:middle !important; margin:2px 5px 2px 1px !important; }

/* RESET BUTTON
=====================================================*/
/* Reset button */
input.reset{
	width:19px; height:19px; cursor:pointer !important;
	border:0 !important; vertical-align:middle;
	background:transparent url(TF_Themes/btn_clear_filters.png) center center no-repeat !important;
}
input.reset:hover{ background:#CAD1D6 url(TF_Themes/btn_clear_filters.png) center center no-repeat !important; }

/* PAGING ELEMENTS
=====================================================*/
.inf{ /* div containing left, middle and right divs */
	clear:both; width:auto; height:25px;
	background:#f4f4f4; font-size:12px;
	margin:0; padding:1px 3px 1px 3px;
	border:1px solid #ccc; overflow:hidden;
}
.ldiv{ /* left div */
	float:left; width:30%; position:inherit;
}
.mdiv{ /* middle div */
	float:left; width:38%; position:inherit; text-align:center;
	padding:1px 0;
}
.rdiv{ /* right div */
	float:right; width:30%; position:inherit; text-align:right;
}
select.pgSlc{ height:20px; vertical-align:middle; font-size:10px; }/*paging drop-down list*/
input.pgNbInp{/*paging text-box*/
	width:25px; height:16px; margin:1px;
	font-size:11px; vertical-align:middle;
}
input.pgInp{ /*paging buttons (Next/Previous)*/
	vertical-align: middle;
	width:19px; height:19px; cursor:pointer !important;
	border:0 !important; font-weight:bold; font-size:11px;
}
.nextPage{ background:transparent url(TF_Themes/btn_next_page.gif) center center no-repeat !important; }
.previousPage{ background:transparent url(TF_Themes/btn_previous_page.gif) center center no-repeat !important; }
.firstPage{ background:transparent url(TF_Themes/btn_first_page.gif) center center no-repeat !important; }
.lastPage{ background:transparent url(TF_Themes/btn_last_page.gif) center center no-repeat !important; }
.nextPage:hover, .previousPage:hover, .firstPage:hover, .lastPage:hover{ background-color:#CAD1D6 !important; }

span.nbpg{ padding:0 5px 0 0; }/*paging nb pages*/
div.inf a{ color:#333; text-decoration:none; font-weight:bold; }/*link appearence in .inf div*/
div.inf a:hover{ text-decoration:underline; }/*link appearence in .inf div*/
div.tot{ /* rows counter appearance */
	float:left; overflow:hidden;
	width:150px; height:15px;
	border-right:1px solid #ccc;
	margin:0 5px 0 2px;
	padding:0 2px 0 2px;
}
div.tot span{ font-weight:bold; font-size:11px; }
select.rspg{ width:50px; margin:1px 5px 0 5px; vertical-align:middle; }/*results per page select*/
span.rspgSpan{ font-size:11px; } /*results per page select label*/

/* BEHAVIOURS
=====================================================*/
span.keyword{ background-color: #ffcc00;}/*highlighted word*/

.even{ background-color:#fff; }/*row bg alternating color*/
.odd{ background-color:#E5E5E5; }/*row bg alternating color*/

.loader{ /* loader appearance */
	position:absolute; padding: 5px 10px 5px 10px;
	margin:20px 0 0 20px; width:auto;
	z-index:1000; font-size:12px; font-weight:bold;
	border:1px solid #666; background:#f4f4f4;
	vertical-align:middle;
}

div.status{ /* status bar appearance */
	float:left; overflow:hidden;
	width:150px; height:15px;
	border-right:1px solid #ccc;
	margin:0 5px 0 2px;
	padding:0 2px 0 2px;
}
div.status span{ font-weight:bold; font-size:11px; }

div.cont{ /*table container div*/
	height:200px; overflow: auto;
}
div.head{ width:auto; overflow:hidden; }

/* Help elements */
.helpBtn{ margin:0 5px 0 5px; padding: 2px 4px 2px 4px; border-left:1px solid #ccc; border-right:1px solid #ccc; } /* help button */
div.helpCont{ /* help container */
	position:absolute; display:none;
	max-width:300px; overflow:auto;
	padding:7px 7px 7px 7px; margin:15px 0;
	border:1px solid #CCC; line-height:19px;
	font-size:12px; color:#333;
	background:#fff; text-align:left;
	border-radius:4px;
	-moz-border-radius:4px;
	-webkit-border-radius:4px;
	box-shadow:3px 3px 2px #888;
	-moz-box-shadow:3px 3px 2px #888;
	-webkit-box-shadow:3px 3px 2px #888;
}
div.helpCont a{ color:#cc0000; text-decoration:underline; font-weight:normal; }
div.helpCont a:hover{ text-decoration:none; }
div.helpCont hr{ border:1px solid #ccc; }
div.helpFooter{ margin:10px 0 0 0; }
div.helpFooter h4{ margin:2px 2px 2px 2px; font-size:13px; color:#333; }

/* Pop-up filters elements */
div.popUpFilter{
	position:absolute; display:none; width:100px;
	background:#C8E0FB; border:1px solid #e4e4e4;
	padding:1px 3px 1px 1px;
	margin:20px auto 0 0px;
}

/* Active column header */
.activeHeader{ background:#004080 !important; color:#fff !important; }

/* GRID LAYOUT
=====================================================*/
div.grd_Cont{ /*Main container*/
	width:800px; height:auto;
	overflow:hidden;
	padding:3px 3px 3px 3px;
	background:#C8E0FB;
	border:1px solid #99BBE8;
}
div.grd_tblCont{ /*Content table container*/
	height:400px; width:800px;
	background:#fff;
	overflow-x:auto;
	overflow-y:scroll;
}
div.grd_headTblCont{ /*headers' table container*/
	height:auto; width:800px; overflow:hidden;
	border-bottom:1px solid #99BBE8;
	background-color:#C8E0FB;
}

div.grd_tblCont table, div.grd_headTblCont table{ /*Tables*/
	font-size:12px; border-collapse:collapse;
	table-layout:fixed;
}
div.grd_tblCont table{ border-right:1px solid #99BBE8; }

/* Filters */
div.grd_Cont .fltrow{ background-color:transparent; } /*Filters' row*/
div.grd_Cont .flt{ border:1px solid #99BBE8; width:97%; }
div.grd_Cont .flt:focus{ border:1px solid #558DD9; }

/* Headers */
div.grd_tblCont table th, div.grd_headTblCont table th, div.grd_headTblCont table td{
	background:#C8E0FB;
	padding:2px 2px 2px 2px !important; color:#333;
	border-right:1px solid #99BBE8 !important;
	overflow:hidden;
	/*white-space:nowrap;*/
	text-overflow:ellipsis;
}
div.grd_tblCont table td{
	padding:2px 2px 2px 2px !important;
	border-bottom:1px solid #ccc !important;
	overflow:hidden;
	/*white-space:nowrap;*/
	text-overflow:ellipsis;
}

.grd_inf{ /* div containing left, middle and right divs */
	clear:both; width:auto; height:25px;
	background:#C8E0FB; font-size:11px;
	margin:0; padding:1px 3px 1px 3px;
	border-top:1px solid #99BBE8;
}

div.grd_Cont .even{ background-color:#fff; }/*row bg alternating color*/
div.grd_Cont .odd{ background-color:#DFE8F6; }/*row bg alternating color*/

/* ezEditTable
=====================================================*/

/* Selection */
.ezActiveRow{ background-color:#2852A8 !important; color:#fff; }
.ezSelectedRow{	background-color:#316AC5; color:#fff; }
.ezActiveCell{
	background-color:#D9E8FB !important;
	color:#000 !important; font-weight:bold;
}
.ezETSelectedCell{ background-color:#FFDC61 !important; font-weight:bold; color:rgb(0,0,0)!important; }
.ezUnselectable{
   -moz-user-select: -moz-none;
   -khtml-user-select: none;
   -webkit-user-select: none;
   -o-user-select: none;
   user-select: none;
}

/* Cell editors */
.ezInputEditor{ width:95%; height:auto; font-size:inherit; border:1px solid #AACCF6; }
.ezTextareaEditor{ width:95%; height:35px; font-size:inherit; border:1px solid #AACCF6; }
.ezSelectEditor{ width:100%; font-size:inherit; border:1px solid #AACCF6; }
.ezModifiedCell{ background:transparent url(themes/bg_mod_cell.png) 0 0 no-repeat; }
select[multiple="multiple"].ezSelectEditor{ height:35px; }
/* Command type editor */
.ezCommandEditor{ margin:2px; }
.ezCommandEditor button, .ezCommandEditor input[type="button"] {
	font-size:11px;	min-height:22px;
	margin:1px; padding:3px;
	border:1px solid #ccc; background:#fff;
	border-radius:4px 4px 4px 4px;
	-moz-border-radius:4px 4px 4px 4px;
}
.ezCommandEditor button:hover, .ezCommandEditor input[type="button"]:hover { border:1px solid #999; }
.ezCommandEditor img{ border:0; vertical-align:middle; margin:2px; }

/* Utils */
.ezOpacity{
	filter:alpha(opacity=60); /* for IE */
	opacity:0.6; /* CSS3 standard */
}
.alignLeft{ text-align:left; }
.alignCenter{ text-align:center; }
.alignRight{ text-align:right; }

/* WEB FX Sortable table
/* Change images paths if you don't use same folder
/* Remove safely if not used
=====================================================*/
.sort-arrow {
	width:11px; height:11px; margin:0 2px;
	background-position:center center;
	background-repeat:no-repeat;
}
.sort-arrow.descending{	background-image:url("TF_Themes/downsimple.png"); }
.sort-arrow.ascending{ background-image:url("TF_Themes/upsimple.png"); }