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()); }}