public void println(String s) {
      super.println(s);
    }
  }

  public void testSystemOutWithActualInstanceMethod() {
    PrintStream originalSystemOut = System.out;
    System.setOut(new MyPublicPrintStream(System.out));
    String expression = "System.out.myPrintln(\"Hello World\");";
    runSingleTest(expression);
    System.setOut(originalSystemOut);
  }

  public static class MyPublicPrintStream extends PrintStream {
    public MyPublicPrintStream(OutputStream os) {
      super(os);
    }

    public void println(String s) {
      super.println(s);
    }

    public void myPrintln(String s) {
      super.println(s);
    }
  }

  public void testMinusOperatorWithoutSpace() {
    String str = "length == $c.length -1";

    ParserConfiguration pconf = new ParserConfiguration();
    ParserContext pctx = new ParserContext(pconf);
    ExecutableStatement stmt = (ExecutableStatement) MVEL.compileExpression(str, pctx);
    Column col1 = new Column("x", 0);
    Column col2 = new Column("x", 0);
    Map<String, Object> vars = new HashMap<String, Object>();
    vars.put("$c", col2);
    Boolean result = (Boolean) MVEL.executeExpression(stmt, col1, vars);
    assertFalse(result);
  }

  public void testDhanji2() {
    String ex =
        "name = null;\n" +
            "$_1 = ?name.?toLowerCase();\n";


    Serializable s = MVEL.compileExpression(ex);

    MVEL.executeExpression(s, new HashMap());
  }

  public void testPrimitiveNumberCoercion() {
    final ParserContext parserContext = new ParserContext();
    parserContext.setStrictTypeEnforcement(true);
    parserContext.setStrongTyping(true);
    parserContext.addInput("a", int.class);
    parserContext.addInput("b", double.class);
    Class<?> clazz = MVEL.analyze("a > b", parserContext);
    assertEquals(Boolean.class, clazz);
  }

  public void testClassLiteral() {
    try {
      MVEL.COMPILER_OPT_SUPPORT_JAVA_STYLE_CLASS_LITERALS = true;
      final ParserContext parserContext = new ParserContext();
      parserContext.setStrictTypeEnforcement(true);
      parserContext.setStrongTyping(true);
      parserContext.addInput("a", Class.class);
      MVEL.compileExpression("a == String", parserContext);
    }
    finally {
      MVEL.COMPILER_OPT_SUPPORT_JAVA_STYLE_CLASS_LITERALS = false;
    }
  }

  public void testBigIntegerWithZeroValue() {
    final ParserContext parserContext = new ParserContext();
    parserContext.setStrictTypeEnforcement(true);
    parserContext.setStrongTyping(true);
    parserContext.addInput("a", BigInteger.class);
    Serializable expression = MVEL.compileExpression("a == 0I", parserContext);
    boolean result = (Boolean) MVEL.executeExpression(expression, new HashMap() {{
      put("a", new BigInteger("0"));
    }});
    assertTrue(result);
  }

  public void testBigDecimalWithZeroValue() {
    final ParserContext parserContext = new ParserContext();
    parserContext.setStrictTypeEnforcement(true);
    parserContext.setStrongTyping(true);
    parserContext.addInput("a", BigDecimal.class);
    Serializable expression = MVEL.compileExpression("a == 0B", parserContext);
    boolean result = (Boolean) MVEL.executeExpression(expression, new HashMap() {{
      put("a", new BigDecimal("0.0"));
    }});
    assertTrue(result);
  }

  public void testMethodReturningPrimitiveTypeAnalysis() {
    String str = "value";

    ParserConfiguration pconf = new ParserConfiguration();
    ParserContext pctx = new ParserContext(pconf);
    pctx.addInput("this", MyObj.class);
    pctx.setStrongTyping(true);

    Class<?> returnType = MVEL.analyze(str, pctx);
    assertEquals(long.class, returnType);
  }

  public static class MyObj {
    public long valueField;

    public MyObj(long value) {
      this.valueField = value;
    }

    public long getValue() {
      return valueField;
    }

    public static String doSomething(MyObj s1, String s2) {
      return s1 + s2;
    }
  }

  public void testStaticMethodsInvocationWithNullArg() {
    String str = "org.mvel2.tests.core.CoreConfidenceTests$MyObj.doSomething(null, \"abc\")";

    ParserConfiguration pconf = new ParserConfiguration();
    ParserContext pctx = new ParserContext(pconf);
    pctx.setStrongTyping(true);

    assertEquals(null + "abc", MVEL.executeExpression(MVEL.compileExpression(str, pctx)));
  }

  public interface InterfaceA {
    InterfaceB getB();
  }

  public interface InterfaceB {
  }

  public static class ImplementationA implements InterfaceA {
    public ImplementationB getB() {
      return new ImplementationB();
    }

    public void setB(InterfaceB b) {
    }
  }

  public static class ImplementationB implements InterfaceB {
    public int getValue() {
      return 42;
    }
  }

  public void testCovariance() {
    final ParserContext parserContext = new ParserContext();
    parserContext.setStrictTypeEnforcement(true);
    parserContext.setStrongTyping(true);
    parserContext.addInput("this", ImplementationA.class);
    assertEquals(int.class, MVEL.analyze("b.value", parserContext));
    assertEquals(42, MVEL.executeExpression(MVEL.compileExpression("b.value", parserContext), new ImplementationA()));
  }

  public void testDivisionType() {
    final ParserContext parserContext = new ParserContext();
    parserContext.setStrictTypeEnforcement(true);
    parserContext.setStrongTyping(true);
    parserContext.addInput("a", BigDecimal.class);
    Serializable expression = MVEL.compileExpression("(a / 3).setScale(2, java.math.BigDecimal.ROUND_HALF_UP)", parserContext);
    Object result = MVEL.executeExpression(expression, new HashMap() {{
      put("a", new BigDecimal("3.0"));
    }});
    System.out.println(result);
  }

  public void testUntypedClone() {
    String expression = "obj.clone();";
    ParserContext context = new ParserContext();
    context.setStrongTyping(false);
    context.setStrictTypeEnforcement(false);
    MVEL.analyze(expression, context);

    try {
      context.addInput("obj", Object.class);
      context.setStrongTyping(true);
      context.setStrictTypeEnforcement(true);
      MVEL.analyze(expression, context);
      fail("Must fail with strong typing");
    }
    catch (CompileException e) {
    }
  }

  public void testOverloading() {
    final ParserContext parserContext = new ParserContext();
    parserContext.setStrictTypeEnforcement(true);
    parserContext.setStrongTyping(true);
    parserContext.addInput("this", Overloaded.class);
    Overloaded overloaded = new Overloaded();

    assertEquals(15, MVEL.executeExpression(MVEL.compileExpression("method(5, 9, \"x\")", parserContext), overloaded));
    assertEquals(-3, MVEL.executeExpression(MVEL.compileExpression("method(5, \"x\", 9)", parserContext), overloaded));
    assertEquals(-13, MVEL.executeExpression(MVEL.compileExpression("method(\"x\", 5, 9)", parserContext), overloaded));
  }

  public static class Overloaded {
    public int method(int i, int j, String s) {
      return i + j + s.length();
    }

    public int method(int i, String s, int j) {
      return i + s.length() - j;
    }

    public int method(String s, int i, int j) {
      return s.length() - i - j;
    }
  }


  public void testPippo() {
    final ParserContext parserContext = new ParserContext();
    parserContext.setStrongTyping(true);
    assertEquals(String.class, MVEL.analyze("new String(\"b)ar\")", parserContext));
  }

  public void testReturnTypeExtendingGeneric() {
    final ParserContext parserContext = new ParserContext();
    parserContext.setStrongTyping(true);
    parserContext.addInput("this", StringConcrete.class);
    assertEquals(String.class, MVEL.analyze("foo.concat(\"bar\")", parserContext));
    assertEquals(String.class, MVEL.analyze("getFoo().concat(\"bar\")", parserContext));
  }

  public static abstract class AbstractBase<T> {
    protected T foo;
    public T getFoo() { return foo; }
  }

  public static class StringConcrete extends AbstractBase<String> {
    public StringConcrete() { this.foo = new String(); }
  }

  public void testNullCollection() throws CompileException {
    boolean allowCompilerOverride = MVEL.COMPILER_OPT_ALLOW_OVERRIDE_ALL_PROPHANDLING;
    MVEL.COMPILER_OPT_ALLOW_OVERRIDE_ALL_PROPHANDLING = true;

    String[] names  = { "missing" };
    String[] values = { null };

    try {
      MVEL.executeExpression( (CompiledExpression) MVEL.compileExpression("1; missing[3]"),
                              new IndexedVariableResolverFactory(names, values) );
      fail("Should throw a NullPointerExcption");
    } catch (Exception e) {
    } finally {
      MVEL.COMPILER_OPT_ALLOW_OVERRIDE_ALL_PROPHANDLING = allowCompilerOverride;
    }
  }

  public void testRegExWithCast() {
    final ParserContext parserContext = new ParserContext();
    parserContext.setStrongTyping(true);
    parserContext.addInput("this", Foo.class);
    assertEquals(Boolean.class, MVEL.analyze("(String)bar.name ~= '[a-z].+'", parserContext));
  }

  public void testUnwantedImport() {
    ParserConfiguration conf = new ParserConfiguration();
    conf.addPackageImport("java.util");
    conf.addPackageImport("org.mvel2.tests.core.res");
    ParserContext pctx = new ParserContext( conf );
    MVEL.analysisCompile( "ScenarioType.Set.ADD", pctx );
    assertNull(conf.getImports().get("Set"));
  }

  public void testUnaryNegative() {
    ParserConfiguration conf = new ParserConfiguration();
    ParserContext pctx = new ParserContext( conf );
    pctx.setStrictTypeEnforcement(true);
    pctx.setStrongTyping(true);
    pctx.addInput("value", int.class);
    Map vars = new HashMap() {{ put("value", 42); }};
    assertEquals(-42, MVEL.executeExpression(MVEL.compileExpression("-value", pctx), vars));
  }

  public void testUnaryNegativeWithSpace() {
    ParserConfiguration conf = new ParserConfiguration();
    ParserContext pctx = new ParserContext( conf );
    pctx.setStrictTypeEnforcement(true);
    pctx.setStrongTyping(true);
    pctx.addInput("value", int.class);
    Map vars = new HashMap() {{ put("value", 42); }};
    assertEquals(-42, MVEL.executeExpression(MVEL.compileExpression("- value", pctx), vars));
  }

  public static class ARef {
    public static int getSize(String s) {
      return 0;
    }
  }

  public static class BRef extends ARef {
    public static int getSize(String s) {
      return s.length();
    }
  }

  public void testStaticMethodInvocation() {
    ParserConfiguration conf = new ParserConfiguration();
    conf.addImport(ARef.class);
    conf.addImport(BRef.class);
    ParserContext pctx = new ParserContext( conf );
    pctx.setStrictTypeEnforcement(true);
    pctx.setStrongTyping(true);
    pctx.addInput("value", String.class);
    Map vars = new HashMap() {{ put("value", "1234"); }};
    assertEquals(0, MVEL.executeExpression(MVEL.compileExpression("ARef.getSize(value)", pctx), vars));
    assertEquals(4, MVEL.executeExpression(MVEL.compileExpression("BRef.getSize(value)", pctx), vars));
  }

  public void testMultiplyIntByDouble() {
    ParserConfiguration conf = new ParserConfiguration();
    ParserContext pctx = new ParserContext( conf );
    pctx.setStrictTypeEnforcement(true);
    pctx.setStrongTyping(true);
    pctx.addInput("i", Integer.class);
    pctx.addInput("d", Double.class);
    Map vars = new HashMap() {{ put("i", 10); put("d", 0.3); }};
    assertEquals(3.0, MVEL.executeExpression(MVEL.compileExpression("i*d", pctx), vars));
    assertEquals(3.0, MVEL.executeExpression(MVEL.compileExpression("i*0.3", pctx), vars));
  }

  public void testCharToStringCoercionForComparison() {
    ParserConfiguration conf = new ParserConfiguration();
    ParserContext pctx = new ParserContext( conf );
    pctx.setStrictTypeEnforcement(true);
    pctx.setStrongTyping(true);
    pctx.addInput("ch", Character.class);
    Map vars = new HashMap() {{ put("ch", 'a'); }};
    assertEquals(true, MVEL.executeExpression(MVEL.compileExpression("ch == \"a\"", pctx), vars));
    assertEquals(false, MVEL.executeExpression(MVEL.compileExpression("ch == \"b\"", pctx), vars));
  }

  public void testFieldNameWithUnderscore() {
      final ParserContext parserContext = new ParserContext();
      parserContext.setStrictTypeEnforcement(true);
      parserContext.setStrongTyping(true);
      parserContext.addInput("this", Underscore.class);
      Underscore underscore = new Underscore();

      assertEquals(true, MVEL.executeExpression(MVEL.compileExpression("_id == \"test\"", parserContext), underscore));
  }

    public static class Underscore {
        public String get_id() {
            return "test";
        }
    }

  public void testEmptyOperatorOnStrings() {
    ParserConfiguration conf = new ParserConfiguration();
    ParserContext pctx = new ParserContext( conf );
    pctx.setStrictTypeEnforcement(true);
    pctx.setStrongTyping(true);

    pctx.addInput("nullString", String.class);
    pctx.addInput("emptyString", String.class);
    pctx.addInput("blankString", String.class);
    pctx.addInput("nonEmptyString", String.class);
    Map vars = new HashMap() {{
        put("nullString", null);
        put("emptyString", "");
        put("blankString", "   ");
        put("nonEmptyString", "abc");
    }};

    assertEquals(true, MVEL.executeExpression(MVEL.compileExpression("nullString == empty", pctx), vars));
    assertEquals(true, MVEL.executeExpression(MVEL.compileExpression("emptyString == empty", pctx), vars));
    assertEquals(true, MVEL.executeExpression(MVEL.compileExpression("blankString == empty", pctx), vars));
    assertEquals(false, MVEL.executeExpression(MVEL.compileExpression("nonEmptyString == empty", pctx), vars));
  }

  public void testEmptyOperatorOnBoolean() {
    ParserConfiguration conf = new ParserConfiguration();
    ParserContext pctx = new ParserContext( conf );
    pctx.setStrictTypeEnforcement(true);
    pctx.setStrongTyping(true);

    pctx.addInput("bNull", Boolean.class);
    pctx.addInput("bTrue", Boolean.class);
    pctx.addInput("bFalse", Boolean.class);
    Map vars = new HashMap() {{
        put("bNull", null);
        put("bTrue", true);
        put("bFalse", false);
    }};

    assertEquals(true, MVEL.executeExpression(MVEL.compileExpression("bNull == empty", pctx), vars));
    assertEquals(false, MVEL.executeExpression(MVEL.compileExpression("bTrue == empty", pctx), vars));
    assertEquals(true, MVEL.executeExpression(MVEL.compileExpression("bFalse == empty", pctx), vars));
  }

  public void testEmptyOperatorOnInteger() {
    ParserConfiguration conf = new ParserConfiguration();
    ParserContext pctx = new ParserContext( conf );
    pctx.setStrictTypeEnforcement(true);
    pctx.setStrongTyping(true);

    pctx.addInput("nullInt", Integer.class);
    pctx.addInput("zero", Integer.class);
    pctx.addInput("nonZero", Integer.class);
    Map vars = new HashMap() {{
        put("nullInt", null);
        put("zero", 0);
        put("nonZero", 42);
    }};

    assertEquals(true, MVEL.executeExpression(MVEL.compileExpression("nullInt == empty", pctx), vars));
    assertEquals(true, MVEL.executeExpression(MVEL.compileExpression("zero == empty", pctx), vars));
    assertEquals(false, MVEL.executeExpression(MVEL.compileExpression("nonZero == empty", pctx), vars));
  }

  public void testInstanceofOnInnerClass() {
    ParserConfiguration conf = new ParserConfiguration();
    conf.addImport(ARef.class);
    ParserContext pctx = new ParserContext( conf );
    pctx.setStrictTypeEnforcement(true);
    pctx.setStrongTyping(true);
    pctx.addInput("value", Object.class);
    Map vars = new HashMap() {{ put("value", new ARef()); }};
    assertEquals(true, MVEL.executeExpression(MVEL.compileExpression("value instanceof ARef", pctx), vars));
    assertEquals(true, MVEL.executeExpression(MVEL.compileExpression("value instanceof " + ARef.class.getCanonicalName(), pctx), vars));
  }

  public void testInstanceofWithPackageImport() {
    ParserConfiguration conf = new ParserConfiguration();
    conf.addPackageImport( "org.mvel2.tests.core" );
    ParserContext pctx = new ParserContext( conf );
    pctx.setStrictTypeEnforcement(true);
    pctx.setStrongTyping(true);
    pctx.addInput("value", Object.class);
    Map vars = new HashMap() {{ put("value", new CoreConfidenceTests()); }};
    assertEquals(true, MVEL.executeExpression(MVEL.compileExpression("value instanceof CoreConfidenceTests", pctx), vars));
    assertEquals(true, MVEL.executeExpression(MVEL.compileExpression("value instanceof " + CoreConfidenceTests.class.getCanonicalName(), pctx), vars));
  }

  public void testInstanceofWithPackageImportAndInnerClass() {
    ParserConfiguration conf = new ParserConfiguration();
    conf.addPackageImport( "org.mvel2.tests.core.CoreConfidenceTests" );
    ParserContext pctx = new ParserContext( conf );
    pctx.setStrictTypeEnforcement(true);
    pctx.setStrongTyping(true);
    pctx.addInput("value", Object.class);
    Map vars = new HashMap() {{ put("value", new ARef()); }};
    assertEquals(true, MVEL.executeExpression(MVEL.compileExpression("value instanceof ARef", pctx), vars));
    assertEquals(true, MVEL.executeExpression(MVEL.compileExpression("value instanceof " + ARef.class.getCanonicalName(), pctx), vars));
  }

  public void testCompilerExceptionFormatting() throws Exception {
    try {
      Object value = test("\n2x * 3\n");
      fail("Invalid expression should fail");
    } catch (Exception e) {
      // Invalid expression should fail to compile
    }
  }

  public void testHandleNumericConversionBug() {
    String[] testLiterals = {"0x20","020",};
    String baseExpression = "int foo = ";

    for( String literal : testLiterals ) {
      char[] decExpr = ( baseExpression + literal ).toCharArray();
      assertEquals( Integer.decode( literal ),
                    ParseTools.handleNumericConversion( decExpr, baseExpression.length(), literal.length() ) );
    }
  }

  public static class Parent {
    public Object getSomething() {
      return null;
    }
  }

  public static class Child extends Parent {
    @Override
    public String getSomething() {
      return null;
    }
  }

  public void testNoArgMethodInheritance() {
    final ParserContext parserContext = new ParserContext();
    parserContext.setStrictTypeEnforcement(true);
    parserContext.setStrongTyping(true);
    parserContext.addInput("a", Parent.class);
    parserContext.addInput("b", Child.class);
    assertEquals(Object.class, MVEL.analyze("a.getSomething()", parserContext));
    assertEquals(String.class, MVEL.analyze("b.getSomething()", parserContext));
  }
  
  
  public void testMethodOverloadMatch() throws Exception {
	  OverloadedClass c = new OverloadedClass();
	  Method found  = ParseTools.getExactMatch("putXX", new Class[]{int.class, String.class}, void.class, OverloadedInterface.class);
	  Method correct = OverloadedInterface.class.getMethod("putXX", new Class[]{int.class, String.class});
	  assertEquals(correct, found);
  }

  public static class O1 {
    public O2 getObj() {
      return new O2();
    }
  }
  public static class O2 extends O3 { }
  public abstract static class O3  {
    public String getValue() {
      return "value";
    }
  }

  public void testInvokeMethodInAbstractClass() {
    final ParserContext parserContext = new ParserContext();
    parserContext.setStrictTypeEnforcement(true);
    parserContext.setStrongTyping(true);
    parserContext.addInput("a", O1.class);
    assertEquals(String.class, MVEL.analyze("a.getObj().getValue()", parserContext));

    Map vars = new HashMap() {{ put("a", new O1()); }}