package com.intellij.compiler.notNullVerification;

import com.intellij.JavaTestUtil;
import com.intellij.compiler.PsiClassWriter;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.testFramework.IdeaTestCase;
import com.intellij.testFramework.UsefulTestCase;
import com.intellij.testFramework.fixtures.IdeaProjectTestFixture;
import com.intellij.testFramework.fixtures.JavaTestFixtureFactory;
import com.intellij.testFramework.fixtures.TestFixtureBuilder;
import org.jetbrains.asm4.ClassReader;
import org.jetbrains.asm4.ClassWriter;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @author yole
 */
public class NotNullVerifyingInstrumenterTest extends UsefulTestCase {
  private IdeaProjectTestFixture myFixture;

  @SuppressWarnings({"JUnitTestCaseWithNonTrivialConstructors"})
  public NotNullVerifyingInstrumenterTest() {
    IdeaTestCase.initPlatformPrefix();
  }

  @Override
  protected void setUp() throws Exception {
    super.setUp();
    final JavaTestFixtureFactory fixtureFactory = JavaTestFixtureFactory.getFixtureFactory();
    final TestFixtureBuilder<IdeaProjectTestFixture> testFixtureBuilder = fixtureFactory.createLightFixtureBuilder();
    myFixture = testFixtureBuilder.getFixture();
    myFixture.setUp();
  }

  @Override
  protected void tearDown() throws Exception {
    myFixture.tearDown();
    super.tearDown();
  }

  public void testSimpleReturn() throws Exception {
    Class testClass = prepareTest();
    Object instance = testClass.newInstance();
    Method method = testClass.getMethod("test");
    verifyCallThrowsException("@NotNull method SimpleReturn.test must not return null", instance, method);
  }

  public void testMultipleReturns() throws Exception {
    Class testClass = prepareTest();
    Object instance = testClass.newInstance();
    Method method = testClass.getMethod("test", int.class);
    verifyCallThrowsException("@NotNull method MultipleReturns.test must not return null", instance, method, 1);
  }

  public void testEnumConstructor() throws Exception {
    Class testClass = prepareTest();
    Object field = testClass.getField("Value");
    assertNotNull(field);
  }

  public void testEnumConstructorSecondParam() throws Exception {
    Class testClass = prepareTest();
    Object field = testClass.getField("Value");
    assertNotNull(field);
  }

  public void testStaticInnerClass() throws Exception {
    final Class aClass = prepareTest();
    assertNotNull(aClass.newInstance());
  }

  public void testNonStaticInnerClass() throws Exception {
    final Class aClass = prepareTest();
    assertNotNull(aClass.newInstance());
  }

  private static void verifyCallThrowsException(final String expectedError, final Object instance, final Method method, final Object... args) throws IllegalAccessException {
    String exceptionText = null;
    try {
      method.invoke(instance, args);
    }
    catch(InvocationTargetException ex) {
      Throwable cause = ex.getCause();
      if (cause instanceof IllegalStateException) {
        exceptionText = cause.getMessage();
      }
    }
    assertEquals(expectedError, exceptionText);
  }

  private Class prepareTest() throws IOException {
    String base = JavaTestUtil.getJavaTestDataPath() + "/compiler/notNullVerification/";
    final String baseClassName = getTestName(false);
    String path = base + baseClassName;
    String javaPath = path + ".java";
    File classesDir = FileUtil.createTempDirectory(baseClassName, "output");

    try {
      com.sun.tools.javac.Main.compile(new String[] { "-classpath", base+"annotations.jar", "-d", classesDir.getAbsolutePath(), javaPath } );

      Class mainClass = null;
      final File[] files = classesDir.listFiles();
      boolean modified = false;
      MyClassLoader classLoader = new MyClassLoader(getClass().getClassLoader());
      for (File file : files) {
        final String fileName = file.getName();
        byte[] content = FileUtil.loadFileBytes(file);

        ClassReader reader = new ClassReader(content, 0, content.length);
        ClassWriter writer = new PsiClassWriter(myFixture.getProject(), false);
        final NotNullVerifyingInstrumenter instrumenter = new NotNullVerifyingInstrumenter(writer);
        reader.accept(instrumenter, 0);
        modified |= instrumenter.isModification();

        byte[] instrumented = writer.toByteArray();
        final String className = FileUtil.getNameWithoutExtension(fileName);
        final Class aClass = classLoader.doDefineClass(className, instrumented);
        if (className.equals(baseClassName)) {
          mainClass = aClass;
        }
      }
      assertTrue(modified);
      assertNotNull("Class " + baseClassName + " not found!", mainClass);
      return mainClass;
    }
    finally {
      FileUtil.delete(classesDir);
    }
  }

  private static class MyClassLoader extends ClassLoader {
    public MyClassLoader(ClassLoader parent) {
      super(parent);
    }


    public Class doDefineClass(String name, byte[] data) {
      return defineClass(name, data, 0, data.length);
    }

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
      return super.loadClass(name);
    }
  }
}

@@ -1,8 +1,609 @@
+/*%0A * Copyright 2000-2012 JetBrains s.r.o.%0A *%0A * Licensed under the Apache License, Version 2.0 (the %22License%22);%0A * you may not use this file except in compliance with the License.%0A * You may obtain a copy of the License at%0A *%0A * http://www.apache.org/licenses/LICENSE-2.0%0A *%0A * Unless required by applicable law or agreed to in writing, software%0A * distributed under the License is distributed on an %22AS IS%22 BASIS,%0A * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.%0A * See the License for the specific language governing permissions and%0A * limitations under the License.%0A */%0A
 package 
@@ -721,24 +721,69 @@
 lassWriter;%0A
+import com.intellij.openapi.util.SystemInfo;%0A
 import com.i
@@ -1420,16 +1420,90 @@
 tCase %7B%0A
+  private final boolean myJava6 = SystemInfo.isJavaVersionAtLeast(%221.6%22);%0A
   privat
@@ -4342,11 +4342,9 @@
 ng%5B%5D
- %7B 
+%7B
 %22-cl
@@ -4357,17 +4357,19 @@
 h%22, base
-+
+ + 
 %22annotat
@@ -4427,11 +4427,9 @@
 Path
- %7D 
+%7D
 );%0A%0A
@@ -4505,24 +4505,52 @@
 istFiles();%0A
+      assertNotNull(files);%0A
       boolea
@@ -4933,21 +4933,23 @@
 ject(), 
-false
+myJava6
 );%0A     
@@ -5763,16 +5763,15 @@
 t);%0A
+
     %7D%0A%0A
-%0A
     

Fix instrumenter tests for JDK7