/*
 * Copyright 2000-2010 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.intellij.lang.ant;

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.util.Pair;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;

import java.lang.ref.SoftReference;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;

/**
* @author Eugene Zhuravlev
*         Date: Apr 9, 2010
*/
public final class ReflectedProject {
  private static final Logger LOG = Logger.getInstance("#com.intellij.lang.ant.ReflectedProject");
  
  @NonNls private static final String INIT_METHOD_NAME = "init";
  @NonNls private static final String GET_TASK_DEFINITIONS_METHOD_NAME = "getTaskDefinitions";
  @NonNls private static final String GET_DATA_TYPE_DEFINITIONS_METHOD_NAME = "getDataTypeDefinitions";
  @NonNls private static final String GET_PROPERTIES_METHOD_NAME = "getProperties";


  private static final List<SoftReference<Pair<ReflectedProject, ClassLoader>>> ourProjects =
    new ArrayList<SoftReference<Pair<ReflectedProject, ClassLoader>>>();

  private static final ReentrantLock ourProjectsLock = new ReentrantLock();
  
  private final Object myProject;
  private Hashtable myTaskDefinitions;
  private Hashtable myDataTypeDefinitions;
  private Hashtable myProperties;
  private Class myTargetClass;

  public static ReflectedProject getProject(final ClassLoader classLoader) {
    ourProjectsLock.lock();
    try {
      for (Iterator<SoftReference<Pair<ReflectedProject, ClassLoader>>> iterator = ourProjects.iterator(); iterator.hasNext();) {
        final SoftReference<Pair<ReflectedProject, ClassLoader>> ref = iterator.next();
        final Pair<ReflectedProject, ClassLoader> pair = ref.get();
        if (pair == null) {
          iterator.remove();
        }
        else {
          if (pair.second == classLoader) {
            return pair.first;
          }
        }
      }
    }
    finally {
      ourProjectsLock.unlock();
    }
    final ReflectedProject reflectedProj = new ReflectedProject(classLoader);
    ourProjectsLock.lock();
    try {
      ourProjects.add(new SoftReference<Pair<ReflectedProject, ClassLoader>>(
        new Pair<ReflectedProject, ClassLoader>(reflectedProj, classLoader)
      ));
    }
    finally {
      ourProjectsLock.unlock();
    }
    return reflectedProj;
  }

  ReflectedProject(final ClassLoader classLoader) {
    Object project = null;
    try {
      final Class projectClass = classLoader.loadClass("org.apache.tools.ant.Project");
      if (projectClass != null) {
        project = projectClass.newInstance();
        Method method = projectClass.getMethod(INIT_METHOD_NAME);
        method.invoke(project);
        method = getMethod(projectClass, GET_TASK_DEFINITIONS_METHOD_NAME);
        myTaskDefinitions = (Hashtable)method.invoke(project);
        method = getMethod(projectClass, GET_DATA_TYPE_DEFINITIONS_METHOD_NAME);
        myDataTypeDefinitions = (Hashtable)method.invoke(project);
        method = getMethod(projectClass, GET_PROPERTIES_METHOD_NAME);
        myProperties = (Hashtable)method.invoke(project);
        myTargetClass = classLoader.loadClass("org.apache.tools.ant.Target");
      }
    }
    catch (ProcessCanceledException e) {
      throw e;
    }
    catch (Throwable e) {
      // rethrow PCE if it was the cause
      Throwable cause = e.getCause();
      Set<Throwable> checked = null; // init lazily; in most cases the set will not be created
      while (cause != null && (checked == null || !checked.contains(cause))) {
        if (cause instanceof ProcessCanceledException) {
          throw (ProcessCanceledException)cause;
        }
        final Throwable nextCause = cause.getCause();
        if (nextCause == null) {
          break;
        }
        if (checked == null) {
          checked = new HashSet<Throwable>();
          checked.add(e);
        }
        checked.add(cause);
        cause = nextCause;
      }

      LOG.info(e);
      project = null;
    }
    myProject = project;
  }

  private static Method getMethod(final Class introspectionHelperClass, final String name) throws NoSuchMethodException {
    final Method method;
    method = introspectionHelperClass.getMethod(name);
    if (!method.isAccessible()) {
      method.setAccessible(true);
    }
    return method;
  }

  @Nullable
  public Hashtable<String, Class> getTaskDefinitions() {
    return myTaskDefinitions;
  }

  @Nullable
  public Hashtable<String, Class> getDataTypeDefinitions() {
    return myDataTypeDefinitions;
  }

  public Hashtable getProperties() {
    return myProperties;
  }

  public Class getTargetClass() {
    return myTargetClass;
  }

  @Nullable
  public Object getProject() {
    return myProject;
  }
}

@@ -775,16 +775,56 @@
 l.Pair;%0A
+import com.intellij.util.ExceptionUtil;%0A
 import o
@@ -986,17 +986,103 @@
 va.util.
-*
+ArrayList;%0Aimport java.util.Hashtable;%0Aimport java.util.Iterator;%0Aimport java.util.List
 ;%0Aimport
@@ -4118,16 +4118,22 @@
 e%0A      
+final 
 Throwabl
@@ -4146,198 +4146,39 @@
 e = 
-e.getCause();%0A      Set%3CThrowable%3E checked = null; // init lazily; in most cases the set will not be created%0A      while (cause != null && (checked == null %7C%7C !checked.contains(cause))) %7B%0A  
+ExceptionUtil.getRootCause(e);%0A
     
@@ -4232,26 +4232,24 @@
 ) %7B%0A        
-  
 throw (Proce
@@ -4285,302 +4285,9 @@
     
-  %7D%0A        final Throwable nextCause = cause.getCause();%0A        if (nextCause == null) %7B%0A          break;%0A        %7D%0A        if (checked == null) %7B%0A          checked = new HashSet%3CThrowable%3E();%0A          checked.add(e);%0A        %7D%0A        checked.add(cause);%0A        cause = nextCause;%0A      %7D%0A
+%7D
 %0A   

simplify code