ClassChecks.java :  » Development » OVal » net » sf » oval » internal » Java Open Source

Java Open Source » Development » OVal 
OVal » net » sf » oval » internal » ClassChecks.java
/*******************************************************************************
 * Portions created by Sebastian Thomschke are copyright (c) 2005-2007 Sebastian
 * Thomschke.
 * 
 * All Rights Reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Sebastian Thomschke - initial implementation.
 *******************************************************************************/
package net.sf.oval.internal;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import java.util.Set;

import net.sf.oval.Check;
import net.sf.oval.exception.InvalidConfigurationException;
import net.sf.oval.guard.IsGuarded;
import net.sf.oval.guard.PostCheck;
import net.sf.oval.guard.PreCheck;
import net.sf.oval.internal.util.ArrayUtils;
import net.sf.oval.internal.util.ReflectionUtils;

/**
 * This class holds the instantiated checks for a single class.
 * 
 * <b>Note:</b> For performance reasons the collections are made public (intended for read-access only).
 * Modifications to the collections should be done through the appropriate methods addXXX, removeXXX, clearXXX methods.
 * 
 * @author Sebastian Thomschke
 */
public class ClassChecks
{
  private static final Log LOG = Log.getLog(ClassChecks.class);

  /**
   * compound constraints / object level invariants
   */
  public final Set<Check> checksForObject = CollectionFactoryHolder.getFactory().createSet(2);

  /**
   * checks on constructors' parameter values
   */
  public final Map<Constructor, Map<Integer, Set<Check>>> checksForConstructorParameters = CollectionFactoryHolder
      .getFactory().createMap(2);

  /**
   * checks on fields' value
   */
  public final Map<Field, Set<Check>> checksForFields = CollectionFactoryHolder.getFactory()
      .createMap();

  /**
   * checks on methods' return value
   */
  public final Map<Method, Set<Check>> checksForMethodReturnValues = CollectionFactoryHolder
      .getFactory().createMap();

  public final Set<Method> methodsWithCheckInvariantsPre = CollectionFactoryHolder.getFactory()
      .createSet();

  public final Set<AccessibleObject> methodsWithCheckInvariantsPost = CollectionFactoryHolder
      .getFactory().createSet();

  /**
   * checks on methods' parameter values
   */
  public final Map<Method, Map<Integer, Set<Check>>> checksForMethodParameters = CollectionFactoryHolder
      .getFactory().createMap();

  public final Map<Method, Set<PostCheck>> checksForMethodsPostExcecution = CollectionFactoryHolder
      .getFactory().createMap();

  public final Map<Method, Set<PreCheck>> checksForMethodsPreExecution = CollectionFactoryHolder
      .getFactory().createMap();

  public final Class clazz;

  /**
   * all non-static fields that have value constraints.
   * Validator loops over this set during validation.
   */
  public final Set<Field> constrainedStaticFields = CollectionFactoryHolder.getFactory()
      .createSet();

  /**
   * all static non-void, non-parameterized methods marked as invariant that have return value constraints.
   * Validator loops over this set during validation.
   */
  public final Set<Method> constrainedStaticMethods = CollectionFactoryHolder.getFactory()
      .createSet();

  /**
   * all non-static fields that have value constraints.
   * Validator loops over this set during validation.
   */
  public final Set<Field> constrainedFields = CollectionFactoryHolder.getFactory().createSet();

  /**
   * all non-static non-void, non-parameterized methods marked as invariant that have return value constraints.
   * Validator loops over this set during validation.
   */
  public final Set<Method> constrainedMethods = CollectionFactoryHolder.getFactory().createSet();

  public final boolean isGuarded;

  public boolean isCheckInvariants;

  /**
   * package constructor used by the Validator class
   * 
   * @param clazz
   */
  @SuppressWarnings("unchecked")
  public ClassChecks(final Class clazz)
  {
    LOG.debug("Initializing constraints configuration for class {}", clazz);

    this.clazz = clazz;
    isGuarded = IsGuarded.class.isAssignableFrom(clazz);
  }

  /**
   * adds constraint checks to a constructor parameter 
   *  
   * @param constructor
   * @param parameterIndex
   * @param checks
   * @throws InvalidConfigurationException if the declaring class is not guarded by GuardAspect 
   */
  public void addConstructorParameterChecks(final Constructor constructor,
      final int parameterIndex, final Check... checks) throws InvalidConfigurationException
  {
    addConstructorParameterChecks(constructor, parameterIndex, (Object) checks);
  }

  /**
   * adds constraint checks to a constructor parameter 
   *  
   * @param constructor
   * @param parameterIndex
   * @param checks
   * @throws InvalidConfigurationException if the declaring class is not guarded by GuardAspect 
   */
  public void addConstructorParameterChecks(final Constructor constructor,
      final int parameterIndex, final Collection<Check> checks)
      throws InvalidConfigurationException
  {
    addConstructorParameterChecks(constructor, parameterIndex, (Object) checks);
  }

  @SuppressWarnings("unchecked")
  private void addConstructorParameterChecks(final Constructor constructor,
      final int parameterIndex, final Object checks) throws InvalidConfigurationException
  {
    if (!isGuarded)
      throw new InvalidConfigurationException(
          "Cannot apply constructor parameter constraints to constructor " + constructor
              + ". Constraints guarding is not activated for this class.");

    final int paramCount = constructor.getParameterTypes().length;

    if (parameterIndex < 0 || parameterIndex >= paramCount)
      throw new InvalidConfigurationException("ParameterIndex " + parameterIndex
          + " is out of range (0-" + (paramCount - 1) + ")");

    synchronized (checksForConstructorParameters)
    {
      // retrieve the currently registered checks for all parameters of the specified constructor
      Map<Integer, Set<Check>> checksOfConstructorByParameter = checksForConstructorParameters
          .get(constructor);
      if (checksOfConstructorByParameter == null)
      {
        checksOfConstructorByParameter = CollectionFactoryHolder.getFactory().createMap(
            paramCount);
        checksForConstructorParameters.put(constructor, checksOfConstructorByParameter);
      }

      // retrieve the checks for the specified parameter
      Set<Check> checksOfConstructorParameter = checksOfConstructorByParameter
          .get(parameterIndex);
      if (checksOfConstructorParameter == null)
      {
        checksOfConstructorParameter = CollectionFactoryHolder.getFactory().createSet(2);
        checksOfConstructorByParameter.put(parameterIndex, checksOfConstructorParameter);
      }

      if (checks instanceof Collection)
      {
        checksOfConstructorParameter.addAll((Collection<Check>) checks);
      }
      else
      {
        ArrayUtils.addAll(checksOfConstructorParameter, (Check[]) checks);
      }
    }
  }

  /**
   * adds check constraints to a field 
   *  
   * @param field
   * @param checks 
   */
  public void addFieldChecks(final Field field, final Check... checks)
      throws InvalidConfigurationException
  {
    addFieldChecks(field, (Object) checks);
  }

  /**
   * adds check constraints to a field 
   *  
   * @param field
   * @param checks 
   */
  public void addFieldChecks(final Field field, final Collection<Check> checks)
      throws InvalidConfigurationException
  {
    addFieldChecks(field, (Object) checks);
  }

  @SuppressWarnings("unchecked")
  private void addFieldChecks(final Field field, final Object checks)
  {
    synchronized (checksForFields)
    {
      Set<Check> checksOfField = checksForFields.get(field);
      if (checksOfField == null)
      {
        checksOfField = CollectionFactoryHolder.getFactory().createSet(2);
        checksForFields.put(field, checksOfField);
        if (ReflectionUtils.isStatic(field))
          constrainedStaticFields.add(field);
        else
          constrainedFields.add(field);
      }

      if (checks instanceof Collection)
      {
        checksOfField.addAll((Collection<Check>) checks);
      }
      else
      {
        ArrayUtils.addAll(checksOfField, (Check[]) checks);
      }
    }
  }

  /**
   * adds constraint checks to a method parameter 
   *  
   * @param method
   * @param parameterIndex
   * @param checks
   * @throws InvalidConfigurationException if the declaring class is not guarded by GuardAspect
   */
  public void addMethodParameterChecks(final Method method, final int parameterIndex,
      final Check... checks) throws InvalidConfigurationException
  {
    addMethodParameterChecks(method, parameterIndex, (Object) checks);
  }

  /**
   * adds constraint checks to a method parameter 
   *  
   * @param method
   * @param parameterIndex
   * @param checks
   * @throws InvalidConfigurationException if the declaring class is not guarded by GuardAspect
   */
  public void addMethodParameterChecks(final Method method, final int parameterIndex,
      final Collection<Check> checks) throws InvalidConfigurationException
  {
    addMethodParameterChecks(method, parameterIndex, (Object) checks);
  }

  @SuppressWarnings("unchecked")
  private void addMethodParameterChecks(final Method method, final int parameterIndex,
      final Object checks) throws InvalidConfigurationException
  {
    if (!isGuarded)
      throw new InvalidConfigurationException(
          "Cannot apply method parameter constraints to class " + clazz.getName()
              + ". Constraints guarding is not activated for this class.");

    final int paramCount = method.getParameterTypes().length;

    if (parameterIndex < 0 || parameterIndex >= paramCount)
      throw new InvalidConfigurationException("ParameterIndex " + parameterIndex
          + " is out of range (0-" + (paramCount - 1) + ")");

    synchronized (checksForMethodParameters)
    {
      // retrieve the currently registered checks for all parameters of the specified method
      Map<Integer, Set<Check>> checksOfMethodByParameter = checksForMethodParameters
          .get(method);
      if (checksOfMethodByParameter == null)
      {
        checksOfMethodByParameter = CollectionFactoryHolder.getFactory().createMap(
            paramCount);
        checksForMethodParameters.put(method, checksOfMethodByParameter);
      }

      // retrieve the checks for the specified parameter
      Set<Check> checksOfMethodParameter = checksOfMethodByParameter.get(parameterIndex);
      if (checksOfMethodParameter == null)
      {
        checksOfMethodParameter = CollectionFactoryHolder.getFactory().createSet(2);
        checksOfMethodByParameter.put(parameterIndex, checksOfMethodParameter);
      }

      if (checks instanceof Collection)
      {
        checksOfMethodParameter.addAll((Collection<Check>) checks);
      }
      else
      {
        ArrayUtils.addAll(checksOfMethodParameter, (Check[]) checks);
      }
    }
  }

  /**
   * adds constraint checks to a method's return value
   * @param method
   * @param checks
   * @throws InvalidConfigurationException if the declaring class is not guarded by GuardAspect
   */
  public void addMethodPostChecks(final Method method, final Collection<PostCheck> checks)
      throws InvalidConfigurationException
  {
    addMethodPostChecks(method, (Object) checks);
  }

  @SuppressWarnings("unchecked")
  private void addMethodPostChecks(final Method method, final Object checks)
      throws InvalidConfigurationException
  {
    if (!isGuarded)
      throw new InvalidConfigurationException("Cannot apply pre condition for method "
          + method + ". Constraints guarding is not activated for this class.");

    synchronized (checksForMethodsPostExcecution)
    {
      Set<PostCheck> postChecks = checksForMethodsPostExcecution.get(method);
      if (postChecks == null)
      {
        postChecks = CollectionFactoryHolder.getFactory().createSet(2);
        checksForMethodsPostExcecution.put(method, postChecks);
      }

      if (checks instanceof Collection)
      {
        postChecks.addAll((Collection<PostCheck>) checks);
      }
      else
      {
        ArrayUtils.addAll(postChecks, (PostCheck[]) checks);
      }
    }
  }

  /**
   * adds constraint checks to a method's return value
   * @param method
   * @param checks
   * @throws InvalidConfigurationException if the declaring class is not guarded by GuardAspect
   */
  public void addMethodPostChecks(final Method method, final PostCheck... checks)
      throws InvalidConfigurationException
  {
    addMethodPostChecks(method, (Object) checks);
  }

  /**
   * @param method
   * @param checks
   * @throws InvalidConfigurationException if the declaring class is not guarded by GuardAspect
   */
  public void addMethodPreChecks(final Method method, final Collection<PreCheck> checks)
      throws InvalidConfigurationException
  {
    addMethodPreChecks(method, (Object) checks);
  }

  @SuppressWarnings("unchecked")
  private void addMethodPreChecks(final Method method, final Object checks)
      throws InvalidConfigurationException
  {
    if (!isGuarded)
      throw new InvalidConfigurationException("Cannot apply pre condition for method "
          + method + ". Constraints guarding is not activated for this class.");

    synchronized (checksForMethodsPreExecution)
    {
      Set<PreCheck> preChecks = checksForMethodsPreExecution.get(method);
      if (preChecks == null)
      {
        preChecks = CollectionFactoryHolder.getFactory().createSet(2);
        checksForMethodsPreExecution.put(method, preChecks);
      }

      if (checks instanceof Collection)
      {
        preChecks.addAll((Collection<PreCheck>) checks);
      }
      else
      {
        ArrayUtils.addAll(preChecks, (PreCheck[]) checks);
      }
    }
  }

  /**
   * @param method
   * @param checks
   * @throws InvalidConfigurationException if the declaring class is not guarded by GuardAspect
   */
  public void addMethodPreChecks(final Method method, final PreCheck... checks)
      throws InvalidConfigurationException
  {
    addMethodPreChecks(method, (Object) checks);
  }

  /**
   * adds constraint checks to a method's return value
   * @param method
   * @param isInvariant determines if the return value should be checked when the object is validated, can be null
   * @param checks
   */
  public void addMethodReturnValueChecks(final Method method, final Boolean isInvariant,
      final Check... checks) throws InvalidConfigurationException
  {
    addMethodReturnValueChecks(method, isInvariant, (Object) checks);
  }

  /**
   * adds constraint checks to a method's return value
   * @param method
   * @param isInvariant determines if the return value should be checked when the object is validated, can be null
   * @param checks
   */
  public void addMethodReturnValueChecks(final Method method, final Boolean isInvariant,
      final Collection<Check> checks) throws InvalidConfigurationException
  {
    addMethodReturnValueChecks(method, isInvariant, (Object) checks);
  }

  @SuppressWarnings("unchecked")
  private void addMethodReturnValueChecks(final Method method, final Boolean isInvariant,
      final Object checks) throws InvalidConfigurationException
  {
    // ensure the method has a return type
    if (method.getReturnType() == Void.TYPE)
      throw new InvalidConfigurationException(
          "Adding return value constraints for method "
              + method
              + " is not possible. The method is declared as void and does not return any values.");

    if (ReflectionUtils.isVoidMethod(method))
      throw new InvalidConfigurationException(
          "Cannot apply method return value constraints for void method " + method);

    final boolean hasParameters = method.getParameterTypes().length > 0;

    if (!isGuarded && hasParameters)
      throw new InvalidConfigurationException(
          "Cannot apply method return value constraints for parameterized method "
              + method + ". Constraints guarding is not activated for this class.");

    final boolean isInvariant2 = isInvariant == null ? constrainedMethods.contains(method)
        : isInvariant;

    if (!isGuarded && !isInvariant2)
      throw new InvalidConfigurationException(
          "Cannot apply method return value constraints for method "
              + method
              + ". The method needs to be marked as being invariant (@IsInvariant) since constraints guarding is not activated for this class.");

    synchronized (checksForMethodReturnValues)
    {
      if (!hasParameters && isInvariant2)
      {
        if (ReflectionUtils.isStatic(method))
          constrainedStaticMethods.add(method);
        else
          constrainedMethods.add(method);
      }
      else
      {
        if (ReflectionUtils.isStatic(method))
          constrainedStaticMethods.remove(method);
        else
          constrainedMethods.remove(method);
      }

      Set<Check> methodChecks = checksForMethodReturnValues.get(method);
      if (methodChecks == null)
      {
        methodChecks = CollectionFactoryHolder.getFactory().createSet(2);
        checksForMethodReturnValues.put(method, methodChecks);
      }

      if (checks instanceof Collection)
      {
        methodChecks.addAll((Collection<Check>) checks);
      }
      else
      {
        ArrayUtils.addAll(methodChecks, (Check[]) checks);
      }
    }
  }

  /**
   * adds check constraints on object level (invariants) 
   *  
   * @param checks 
   */
  public void addObjectChecks(final Check... checks)
  {
    synchronized (checksForObject)
    {
      ArrayUtils.addAll(checksForObject, checks);
    }
  }

  /**
   * adds check constraints on object level (invariants) 
   *  
   * @param checks 
   */
  public void addObjectChecks(final Collection<Check> checks)
  {
    synchronized (checksForObject)
    {
      checksForObject.addAll(checks);
    }
  }

  public synchronized void clear()
  {
    LOG.debug("Clearing all checks for class {}", clazz);

    checksForObject.clear();
    checksForMethodsPostExcecution.clear();
    checksForMethodsPreExecution.clear();
    checksForConstructorParameters.clear();
    checksForFields.clear();
    checksForMethodReturnValues.clear();
    checksForMethodParameters.clear();
    constrainedFields.clear();
    constrainedStaticFields.clear();
    constrainedMethods.clear();
    constrainedStaticMethods.clear();
  }

  public void clearConstructorChecks(final Constructor constructor)
  {
    clearConstructorParameterChecks(constructor);
  }

  public void clearConstructorParameterChecks(final Constructor constructor)
  {
    synchronized (checksForConstructorParameters)
    {
      checksForConstructorParameters.remove(constructor);
    }
  }

  public void clearConstructorParameterChecks(final Constructor constructor,
      final int parameterIndex)
  {
    synchronized (checksForConstructorParameters)
    {
      // retrieve the currently registered checks for all parameters of the specified method
      final Map<Integer, Set<Check>> checksOfConstructorByParameter = checksForConstructorParameters
          .get(constructor);
      if (checksOfConstructorByParameter == null) return;

      // retrieve the checks for the specified parameter
      final Collection<Check> checksOfMethodParameter = checksOfConstructorByParameter
          .get(parameterIndex);
      if (checksOfMethodParameter == null) return;

      checksOfConstructorByParameter.remove(parameterIndex);
    }
  }

  public void clearFieldChecks(final Field field)
  {
    synchronized (checksForFields)
    {
      checksForFields.remove(field);
      constrainedFields.remove(field);
      constrainedStaticFields.remove(field);
    }
  }

  public synchronized void clearMethodChecks(final Method method)
  {
    clearMethodParameterChecks(method);
    clearMethodReturnValueChecks(method);
    clearMethodPreChecks(method);
    clearMethodPostChecks(method);
  }

  public void clearMethodParameterChecks(final Method method)
  {
    synchronized (checksForMethodParameters)
    {
      checksForMethodParameters.remove(method);
    }
  }

  public void clearMethodParameterChecks(final Method method, final int parameterIndex)
  {
    synchronized (checksForMethodParameters)
    {
      // retrieve the currently registered checks for all parameters of the specified method
      final Map<Integer, Set<Check>> checksOfMethodByParameter = checksForMethodParameters
          .get(method);
      if (checksOfMethodByParameter == null) return;

      // retrieve the checks for the specified parameter
      final Collection<Check> checksOfMethodParameter = checksOfMethodByParameter
          .get(parameterIndex);
      if (checksOfMethodParameter == null) return;

      checksOfMethodByParameter.remove(parameterIndex);
    }
  }

  public void clearMethodPostChecks(final Method method)
  {
    synchronized (checksForMethodsPostExcecution)
    {
      checksForMethodsPostExcecution.remove(method);
    }
  }

  public void clearMethodPreChecks(final Method method)
  {
    synchronized (checksForMethodsPreExecution)
    {
      checksForMethodsPreExecution.remove(method);
    }
  }

  public void clearMethodReturnValueChecks(final Method method)
  {
    synchronized (checksForMethodReturnValues)
    {
      checksForMethodReturnValues.remove(method);
      constrainedMethods.remove(method);
      constrainedStaticMethods.remove(method);
    }
  }

  public void clearObjectChecks()
  {
    synchronized (checksForObject)
    {
      checksForObject.clear();
    }
  }

  public void removeConstructorParameterChecks(final Constructor constructor,
      final int parameterIndex, final Check... checks)
  {
    synchronized (checksForConstructorParameters)
    {
      // retrieve the currently registered checks for all parameters of the specified method
      final Map<Integer, Set<Check>> checksOfConstructorByParameter = checksForConstructorParameters
          .get(constructor);
      if (checksOfConstructorByParameter == null) return;

      // retrieve the checks for the specified parameter
      final Collection<Check> checksOfConstructorParameter = checksOfConstructorByParameter
          .get(parameterIndex);
      if (checksOfConstructorParameter == null) return;

      for (final Check check : checks)
      {
        checksOfConstructorParameter.remove(check);
      }

      if (checksOfConstructorParameter.size() == 0)
      {
        checksOfConstructorByParameter.remove(parameterIndex);
      }
    }
  }

  public void removeFieldChecks(final Field field, final Check... checks)
  {
    synchronized (checksForFields)
    {
      final Set<Check> checksOfField = checksForFields.get(field);

      if (checksOfField == null) return;

      for (final Check check : checks)
      {
        checksOfField.remove(check);
      }

      if (checksOfField.size() == 0)
      {
        checksForFields.remove(field);
        constrainedFields.remove(field);
        constrainedStaticFields.remove(field);
      }
    }
  }

  public void removeMethodChecks(final Method method, final Check... checks)
  {
    synchronized (checksForMethodReturnValues)
    {
      final Set<Check> checksOfMethod = checksForMethodReturnValues.get(method);

      if (checksOfMethod == null) return;

      for (final Check check : checks)
      {
        checksOfMethod.remove(check);
      }

      if (checksOfMethod.size() == 0)
      {
        checksForMethodReturnValues.remove(method);
        constrainedMethods.remove(method);
        constrainedStaticMethods.remove(method);
      }
    }
  }

  public void removeMethodParameterChecks(final Method method, final int parameterIndex,
      final Check... checks) throws InvalidConfigurationException
  {
    if (parameterIndex < 0 || parameterIndex > method.getParameterTypes().length)
      throw new InvalidConfigurationException("ParameterIndex is out of range");

    synchronized (checksForMethodParameters)
    {
      // retrieve the currently registered checks for all parameters of the specified method
      final Map<Integer, Set<Check>> checksOfMethodByParameter = checksForMethodParameters
          .get(method);
      if (checksOfMethodByParameter == null) return;

      // retrieve the checks for the specified parameter
      final Collection<Check> checksOfMethodParameter = checksOfMethodByParameter
          .get(parameterIndex);
      if (checksOfMethodParameter == null) return;

      for (final Check check : checks)
      {
        checksOfMethodParameter.remove(check);
      }

      if (checksOfMethodParameter.size() == 0)
      {
        checksOfMethodByParameter.remove(parameterIndex);
      }
    }
  }

  public void removeMethodPostChecks(final Method method, final PostCheck... checks)
  {
    synchronized (checksForMethodsPostExcecution)
    {
      final Set<PostCheck> checksforMethod = checksForMethodsPostExcecution.get(method);

      if (checksforMethod == null) return;

      for (final PostCheck check : checks)
      {
        checksforMethod.remove(check);
      }

      if (checksforMethod.size() == 0)
      {
        checksForMethodsPostExcecution.remove(method);
      }
    }
  }

  public void removeMethodPreChecks(final Method method, final PreCheck... checks)
  {
    synchronized (checksForMethodsPreExecution)
    {
      final Set<PreCheck> checksforMethod = checksForMethodsPreExecution.get(method);

      if (checksforMethod == null) return;

      for (final PreCheck check : checks)
      {
        checksforMethod.remove(check);
      }

      if (checksforMethod.size() == 0)
      {
        checksForMethodsPreExecution.remove(method);
      }
    }
  }

  public void removeObjectChecks(final Check... checks)
  {
    synchronized (checksForObject)
    {
      for (final Check check : checks)
      {
        checksForObject.remove(check);
      }
    }
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.