/*
 *    Copyright (c) 2008-2011 Graham Allan
 *
 *   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 org.mutabilitydetector;

import static com.google.common.collect.Lists.newArrayList;
import static org.mutabilitydetector.checkers.info.AnalysisDatabase.newAnalysisDatabase;
import static org.mutabilitydetector.locations.Dotted.dotted;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.mutabilitydetector.asmoverride.AsmVerifierFactory;
import org.mutabilitydetector.asmoverride.CachingTypeHierarchyReader;
import org.mutabilitydetector.asmoverride.ClassLoadingVerifierFactory;
import org.mutabilitydetector.asmoverride.IsAssignableFromCachingTypeHierarchyReader;
import org.mutabilitydetector.asmoverride.NonClassLoadingVerifierFactory;
import org.mutabilitydetector.checkers.AsmSessionCheckerRunner;
import org.mutabilitydetector.checkers.info.AnalysisDatabase;
import org.mutabilitydetector.checkers.info.MutableTypeInformation;
import org.mutabilitydetector.checkers.info.SessionCheckerRunner;
import org.mutabilitydetector.locations.Dotted;
import org.objectweb.asm.tree.analysis.TypeHierarchyReader;

import com.google.classpath.ClassPath;
import com.google.classpath.ClassPathFactory;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;

public final class ThreadUnsafeAnalysisSession implements AnalysisSession, AnalysisErrorReporter {

    private final Cache<Dotted, AnalysisResult> analysedClasses = CacheBuilder.newBuilder().recordStats().build();
    private final List<AnalysisErrorReporter.AnalysisError> analysisErrors = newArrayList();

    private final MutabilityCheckerFactory checkerFactory;
    private final CheckerRunnerFactory checkerRunnerFactory;
    private final AnalysisDatabase database;
    private final AsmVerifierFactory verifierFactory;
    private final MutableTypeInformation mutableTypeInformation;
    
    private ThreadUnsafeAnalysisSession(ClassPath classpath, 
                             CheckerRunnerFactory checkerRunnerFactory,
                             MutabilityCheckerFactory checkerFactory, 
                             AsmVerifierFactory verifierFactory,
                             Configuration configuration) {
        this.checkerRunnerFactory = checkerRunnerFactory;
        this.checkerFactory = checkerFactory;
        this.verifierFactory = verifierFactory;
        AsmSessionCheckerRunner sessionCheckerRunner = new SessionCheckerRunner(this, checkerRunnerFactory.createRunner());
        mutableTypeInformation = new MutableTypeInformation(this, configuration);
        this.database = newAnalysisDatabase(sessionCheckerRunner);
    }

    public static AnalysisSession createWithGivenClassPath(ClassPath classpath, 
                                                              CheckerRunnerFactory checkerRunnerFactory,
                                                              MutabilityCheckerFactory checkerFactory, 
                                                              AsmVerifierFactory verifierFactory,
                                                              Configuration configuration) {
        return createWithGivenClassPath(classpath, configuration, verifierFactory);
    }

    public static AnalysisSession createWithCurrentClassPath() {
        return createWithCurrentClassPath(ConfigurationBuilder.NO_CONFIGURATION);
    }
    
    public static AnalysisSession createWithCurrentClassPath(Configuration configuration) {
        ClassPath classpath = new ClassPathFactory().createFromJVM();
        ClassLoadingVerifierFactory verifierFactory = new ClassLoadingVerifierFactory(new CachingAnalysisClassLoader(new ClassForNameWrapper()));
        return createWithGivenClassPath(classpath, configuration, verifierFactory);
    }
    
    public static AnalysisSession tempCreateWithVerifier() {
        ClassPath classpath = new ClassPathFactory().createFromJVM();
        AsmVerifierFactory verifierFactory = new NonClassLoadingVerifierFactory(
                new IsAssignableFromCachingTypeHierarchyReader(
                        new CachingTypeHierarchyReader(new TypeHierarchyReader())));
        return createWithGivenClassPath(classpath, ConfigurationBuilder.NO_CONFIGURATION, verifierFactory);
        
    }

    private static AnalysisSession createWithGivenClassPath(ClassPath classpath, Configuration configuration, AsmVerifierFactory verifierFactory) {
        return new ThreadUnsafeAnalysisSession(classpath, 
                                    new ClassPathBasedCheckerRunnerFactory(classpath, configuration.exceptionPolicy()), 
                                    new MutabilityCheckerFactory(), 
                                    verifierFactory,
                                    configuration);
    }

    @Override
    public AnalysisResult resultFor(Dotted className) {
        return requestAnalysis(className);
    }

    private AnalysisResult requestAnalysis(Dotted className) {
        AnalysisResult existingResult = analysedClasses.getIfPresent(className);
        if (existingResult != null) {
            return existingResult;
        }
        
        AllChecksRunner allChecksRunner = new AllChecksRunner(checkerFactory,
                                                              checkerRunnerFactory,
                                                              verifierFactory, 
                                                              className);
        
        return addAnalysisResult(allChecksRunner.runCheckers(this, this, database, mutableTypeInformation));
    }

    private AnalysisResult addAnalysisResult(AnalysisResult result) {
        analysedClasses.put(dotted(result.dottedClassName), result);
        return result;
    }

    @Override
    public void addAnalysisError(AnalysisError error) {
        analysisErrors.add(error);
    }

    @Override
    public Collection<AnalysisResult> getResults() {
        return Collections.unmodifiableCollection(analysedClasses.asMap().values());
    }

    @Override
    public Map<Dotted, AnalysisResult> resultsByClass() {
        return Collections.unmodifiableMap(analysedClasses.asMap());
    }

    @Override
    public Collection<AnalysisError> getErrors() {
        return Collections.unmodifiableCollection(analysisErrors);
    }

    @Override
    public AnalysisErrorReporter errorReporter() {
        return this;
    }
}

@@ -2515,59 +2515,8 @@
 ion(
-ClassPath classpath, %0A                             
 Chec
@@ -2545,32 +2545,33 @@
 erRunnerFactory,
+ 
 %0A               
@@ -2616,33 +2616,32 @@
  checkerFactory,
- 
 %0A               
@@ -2681,32 +2681,33 @@
 verifierFactory,
+ 
 %0A               
@@ -3254,37 +3254,33 @@
 sPath classpath,
- %0A   
+%0A
                 
@@ -3416,35 +3416,32 @@
                 
-   
 MutabilityChecke
@@ -3456,37 +3456,33 @@
  checkerFactory,
- %0A   
+%0A
                 
@@ -3552,35 +3552,32 @@
 erifierFactory,%0A
-   
                 
@@ -4771,24 +4771,16 @@
 ctory);%0A
-        
 %0A    %7D%0A%0A
@@ -4863,37 +4863,157 @@
 ath,
- Configuration configuration,
+%0A                                                            Configuration configuration,%0A                                                           
  Asm
@@ -5098,56 +5098,8 @@
 ion(
-classpath, %0A                                    
 new 
@@ -5211,24 +5211,35 @@
             
+           
 new Mutabili
@@ -5287,32 +5287,43 @@
                 
+           
 verifierFactory,
@@ -5314,33 +5314,45 @@
 verifierFactory,
-%0A
+ %0A           
                 

Remove unnecessary parameter.