All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.staccatocommons.restrictions.instrument.check.AbstractCheckAnnotationHandler Maven / Gradle / Ivy

The newest version!
/**
 *  Copyright (c) 2011, The Staccato-Commons Team
 *
 *  This program 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; version 3 of the License.
 *
 *  This program 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.
 */

package net.sf.staccatocommons.restrictions.instrument.check;

import java.lang.annotation.Annotation;

import javassist.CannotCompileException;
import javassist.CtBehavior;
import javassist.NotFoundException;
import net.sf.staccatocommons.check.Ensure;
import net.sf.staccatocommons.instrument.context.AnnotationContext;
import net.sf.staccatocommons.instrument.context.ArgumentAnnotationContext;
import net.sf.staccatocommons.instrument.context.MethodAnnotationContext;
import net.sf.staccatocommons.instrument.handler.ArgumentAnnotationHandler;
import net.sf.staccatocommons.instrument.handler.MethodAnnotationHandler;
import net.sf.staccatocommons.instrument.handler.deactivator.Deactivable;
import net.sf.staccatocommons.instrument.handler.deactivator.StackedDeactivableSupport;
import net.sf.staccatocommons.lang.SoftException;

/**
 * @author flbulgarelli
 * 
 */
public abstract class AbstractCheckAnnotationHandler implements MethodAnnotationHandler,
  ArgumentAnnotationHandler, Deactivable {

  protected static final String ENSURE_FULLY_QUALIFIED_NAME = "net.sf.staccatocommons.check.Ensure.";
  protected static final String ASSERT_FULLY_QUALIFIED_NAME = "net.sf.staccatocommons.check.Assert.";
  private final StackedDeactivableSupport deactivableSupport = new StackedDeactivableSupport();
  private final boolean ignoreReturns;

  /**
   * Creates a new {@link AbstractCheckAnnotationHandler} specifying if check
   * annotation in methods should be interpreted as checks over its return value
   * and processed
   * 
   * @param ignoreReturns
   *          if check annotation over returns should be processed
   */
  public AbstractCheckAnnotationHandler(boolean ignoreReturns) {
    this.ignoreReturns = ignoreReturns;
  }

  @Override
  public void processAnnotatedArgument(T annotation, ArgumentAnnotationContext context) throws CannotCompileException,
    NotFoundException {
    if (!isActive(context))
      return;
    try {
      context.getArgumentBehavior().insertBefore(
        ENSURE_FULLY_QUALIFIED_NAME + createArgumentCheck(annotation, context) + ";");
    } catch (CannotCompileException e) {
      logError(context, context.getArgumentBehavior(), e);
      throw e;
    }
  }

  @Override
  public void preProcessAnnotatedMethod(T annotation, MethodAnnotationContext context) throws NotFoundException {
    if (ignoreReturns || !isActive(context))
      return;
    // TODO handle properly
    Ensure.that(!context.isVoid(), "Context must not be void: %s", context.getMethod().getLongName());
    try {
      context.getMethod().insertAfter(ASSERT_FULLY_QUALIFIED_NAME + createMethodCheck(annotation, context) + ";");
    } catch (CannotCompileException e) {
      logError(context, context.getMethod(), e);
      throw SoftException.soften(e);
    }
  }

  private void logError(AnnotationContext context, CtBehavior be, CannotCompileException e) {
    context.logErrorMessage("Could not insert argument check on method {}: {}", //
      be.getLongName(),
      e.getMessage());
  }

  @Override
  public void postProcessAnnotatedMethod(T annotation, MethodAnnotationContext context) {}

  protected String createArgumentCheck(T annotation, ArgumentAnnotationContext context) throws NotFoundException {
    return createCheckCode(
      getArgumentMnemonic(context, getVarMnemonic(annotation)),
      context.getArgumentIdentifier(),
      annotation,
      context);
  }

  private String getArgumentMnemonic(ArgumentAnnotationContext context, String annotatedVarName) {
    return annotatedVarName.isEmpty() ? "var" + context.getArgumentNumber() : annotatedVarName;
  }

  private String createMethodCheck(T annotation, MethodAnnotationContext context) throws NotFoundException {
    return createCheckCode(
      getReturnName(getVarMnemonic(annotation)),
      context.getReturnIdentifier(),
      annotation,
      context);
  }

  protected String getReturnName(String returnName) {
    return returnName.isEmpty() ? "returnValue" : returnName;
  }

  public final void deactivate() {
    deactivableSupport.deactivate();
  }

  public final void activate() {
    deactivableSupport.activate();
  }

  protected abstract String createCheckCode(String argumentMnemonic, String argumentIdentifier, T annotation,
    AnnotationContext context) throws NotFoundException;

  protected abstract String getVarMnemonic(T annotation);

  protected boolean activeByDefault(AnnotationContext context) {
    return context.isPublic() && !context.getPackage().matches(".+\\.internal(\\..+)?");
  }
  
  private boolean isActive(AnnotationContext context) {
    boolean activeByDefault = activeByDefault(context);
    if (activeByDefault)
      deactivableSupport.activate();
    try {
      return deactivableSupport.isActive();
    } finally {
      if (activeByDefault)
        deactivableSupport.deactivate();
    }
  }

  
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy