/*
 * FindBugs - Find Bugs in Java programs
 * Copyright (C) 2003-2008 University of Maryland
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package edu.umd.cs.findbugs.ba.jsr305;

import java.lang.annotation.Annotation;
import java.security.Permission;

import javax.annotation.CheckForNull;
import javax.annotation.meta.TypeQualifierValidator;
import javax.annotation.meta.When;

final class ValidationSecurityManager extends SecurityManager {


    static final ValidationSecurityManager INSTANCE = new ValidationSecurityManager();

   
    
    static {
        if (TypeQualifierValue.DEBUG_CLASSLOADING)
            new RuntimeException("Creating ValidationSecurityManager #").printStackTrace();

    }
    public static <A extends Annotation> When sandboxedValidation(A proxy, TypeQualifierValidator<A> v, @CheckForNull
    Object constantValue) {
        if (performingValidation.get())
            throw new IllegalStateException("recursive validation");

        try {
            performingValidation.set(Boolean.TRUE);
            if (TypeQualifierValue.DEBUG_CLASSLOADING)
                System.out.println("Performing validation in thread " + Thread.currentThread().getName());
            try {
                When result = v.forConstantValue(proxy, constantValue);
                if (!performingValidation.get())
                    throw new IllegalStateException("performingValidation not set when validation completes");
                return result;
            } catch (ClassCastException e) {
                Class<? extends Annotation> c = proxy.getClass();
                System.out.println(c.getName() + " extends " + c.getSuperclass().getName());
                for(Class<?> i : c.getInterfaces())
                        System.out.println("  " + i.getName());
                throw e;
            }
            
        } finally {
            performingValidation.set(Boolean.FALSE);
            if (TypeQualifierValue.DEBUG_CLASSLOADING)
                System.out.println("Validation finished in thread " + Thread.currentThread().getName());
           
        }
    }


    @Override
    public void checkPermission(Permission perm) {
        if (TypeQualifierValue.DEBUG_CLASSLOADING)
            System.out.println("Checking for " + perm + " permission in thread " + Thread.currentThread().getName());
        if (performingValidation.get() && inValidation()) {
            SecurityException e = new SecurityException("No permissions granted while performing JSR-305 validation");
            if (TypeQualifierValue.DEBUG_CLASSLOADING)
                e.printStackTrace(System.out);
            throw e;
        }

    }

    @Override
    public void checkPermission(Permission perm, Object context) {
        if (TypeQualifierValue.DEBUG_CLASSLOADING)
            System.out.println("Checking for " + perm + " permission with content in thread " + Thread.currentThread().getName());
        
        if (performingValidation.get() && inValidation()) {
            SecurityException e = new SecurityException("No permissions granted while performing JSR-305 validation");
            if (TypeQualifierValue.DEBUG_CLASSLOADING)
                e.printStackTrace(System.out);
            throw e;
        }
    }

    private ValidationSecurityManager() { }

    private boolean inValidation() {
        for (Class<?> c : getClassContext()) {
            if (TypeQualifierValidator.class.isAssignableFrom(c)
                    || c.getClassLoader() == ValidatorClassLoader.INSTANCE)
                return true;
        }
        return false;
    }

    private static final ThreadLocal<Boolean> performingValidation = new ThreadLocal<Boolean>() {
        @Override
        protected Boolean initialValue() {
            return Boolean.FALSE;
        }

    };


}