fixed pointcut for type-level annotation to only apply to public methods (SPR-8890)

/*
 * Copyright 2002-2010 the original author or authors.
 *
 * 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.springframework.transaction.aspectj;

import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
import org.springframework.transaction.annotation.Transactional;

/**
 * Concrete AspectJ transaction aspect using Spring's @Transactional annotation.
 *
 *
When using this aspect, you must annotate the implementation class
 * (and/or methods within that class), not the interface (if any) that
 * the class implements. AspectJ follows Java's rule that annotations on
 * interfaces are not inherited.
 *
 *
An @Transactional annotation on a class specifies the default transaction
 * semantics for the execution of any public operation in the class.
 *
 *
An @Transactional annotation on a method within the class overrides the
 * default transaction semantics given by the class annotation (if present).
 * Any method may be annotated (regardless of visibility).
 * Annotating non-public methods directly is the only way
 * to get transaction demarcation for the execution of such operations.
 *
 * @author Rod Johnson
 * @author Ramnivas Laddad
 * @author Adrian Colyer
 * @since 2.0
 * @see org.springframework.transaction.annotation.Transactional
 */
public aspect AnnotationTransactionAspect extends AbstractTransactionAspect {

	public AnnotationTransactionAspect() {
		super(new AnnotationTransactionAttributeSource(false));
	}

	/**
	 * Matches the execution of any public method in a type with the
	 * Transactional annotation, or any subtype of a type with the
	 * Transactional annotation.
	 */
	private pointcut executionOfAnyPublicMethodInAtTransactionalType() :
		execution(public * ((@Transactional *)+).*(..)) && within(@Transactional);

	/**
	 * Matches the execution of any method with the
	 * Transactional annotation.
	 */
	private pointcut executionOfTransactionalMethod() :
		execution(@Transactional * *(..));

	/**
	 * Definition of pointcut from super aspect - matched join points
	 * will have Spring transaction management applied.
	 */
	protected pointcut transactionalMethodExecution(Object txObject) :
		(executionOfAnyPublicMethodInAtTransactionalType()
		 || executionOfTransactionalMethod() )
		 && this(txObject);

}

/*
 * Copyright 2002-2010 the original author or authors.
 *
 * 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.springframework.transaction.aspectj;

import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
import org.springframework.transaction.annotation.Transactional;

/**
 * Concrete AspectJ transaction aspect using Spring's @Transactional annotation.
 *
 *
When using this aspect, you must annotate the implementation class
 * (and/or methods within that class), not the interface (if any) that
 * the class implements. AspectJ follows Java's rule that annotations on
 * interfaces are not inherited.
 *
 *
An @Transactional annotation on a class specifies the default transaction
 * semantics for the execution of any public operation in the class.
 *
 *
An @Transactional annotation on a method within the class overrides the
 * default transaction semantics given by the class annotation (if present).
 * Any method may be annotated (regardless of visibility).
 * Annotating non-public methods directly is the only way
 * to get transaction demarcation for the execution of such operations.
 *
 * @author Rod Johnson
 * @author Ramnivas Laddad
 * @author Adrian Colyer
 * @since 2.0
 * @see org.springframework.transaction.annotation.Transactional
 */
public aspect AnnotationTransactionAspect extends AbstractTransactionAspect {

	public AnnotationTransactionAspect() {
		super(new AnnotationTransactionAttributeSource(false));
	}

	/**
	 * Matches the execution of any public method in a type with the
	 * Transactional annotation, or any subtype of a type with the
	 * Transactional annotation.
	 */
	private pointcut executionOfAnyPublicMethodInAtTransactionalType() :
		execution(* *(..)) && within(@Transactional *);

	/**
	 * Matches the execution of any method with the
	 * Transactional annotation.
	 */
	private pointcut executionOfTransactionalMethod() :
		execution(@Transactional * *(..));

	/**
	 * Definition of pointcut from super aspect - matched join points
	 * will have Spring transaction management applied.
	 */
	protected pointcut transactionalMethodExecution(Object txObject) :
		(executionOfAnyPublicMethodInAtTransactionalType()
		 || executionOfTransactionalMethod() )
		 && this(txObject);

}


txtUML multiproject: small fix in export.uml2.

package hu.elte.txtuml.export.uml2.transform.aspects;

import hu.elte.txtuml.api.ModelElement;
import hu.elte.txtuml.api.Action;
import hu.elte.txtuml.api.Collection;
import hu.elte.txtuml.export.uml2.utils.ImportWarningProvider;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.LinkedList;

import org.aspectj.lang.annotation.SuppressAjWarnings;

/**
 * This aspect contains advices supporting model import.
 * @author Adam Ancsin
 *
 */
public privileged aspect ImporterSupportingAspect extends AbstractImporterAspect {

	/**
	 * This advice shows an error message when a non-txtUML method is called from a method body
	 * during model import.
	 *
	 * @author Adam Ancsin
	 */
	@SuppressAjWarnings
	Object around() :
		call(* (!ModelElement+).*(..)) &&
		!call(* (java.lang..*).*(..)) &&
		!call(* (Collection+).*(..))
		&& isActive()
	{
		ImportWarningProvider.createWarning(
				"non-txtUML method call - " +
				thisJoinPoint.getSignature().getDeclaringType().getName() +
				"." +
				thisJoinPoint.getSignature().getName()
			);
		return proceed();
	}

	/**
	 * This advice hides all the synthetic methods from the result of Class.getDeclaredMethods() calls.
	 * It is needed to hide the private methods generated by AspectJ.
	 *
	 * @author Adam Ancsin
	 */
	@SuppressAjWarnings
	Method[] around(Object c) : target(c) && call(Method[] Class.getDeclaredMethods()) && withinExportUML2() {
		LinkedList<Method> methods = new LinkedList<>();
		for(Method m : proceed(c)) {
			if (!m.isSynthetic()) {
				methods.add(m);
			}
		}
		return methods.toArray(new Method[0]);
	}

	/**
	 * This advice hides all the synthetic fields from the result of Class.getDeclaredFields() calls.
	 * It is needed to hide the fields generated by the Java compiler. (e.g. this$0)
	 *
	 * @author Adam Ancsin
	 */
	@SuppressAjWarnings
	Field[] around(Object c) : target(c) && call(Field[] Class.getDeclaredFields()) && withinExportUML2() {
		LinkedList<Field> fields = new LinkedList<>();
		for(Field f : proceed(c)) {
			if (!f.isSynthetic()) {
				fields.add(f);
			}
		}
		return fields.toArray(new Field[0]);
	}

	/**
	 * This advice prevents execution of Action.start if called from outside the model (e.g. from glue code)
	 * during model import, so none of the ModelClass instances start functioning.
	 *
	 * @author Adam Ancsin
	 */
	@SuppressAjWarnings
	void around(): call(void Action.start(..)) && importing() && !withinModel()
	{
		//do nothing
	}

	/**
	 * This advice prevents logging in models during model import.
	 *
	 * @author Adam Ancsin
	 */
	@SuppressAjWarnings
	void around():
		(
			call(void Action.log(..)) ||
			call(void Action.logError(..)) ||
			call(void ModelExecutor.executorLog(..)) ||
			call(void ModelExecutor.executorErrorLog(..))
		)
		&& isActive()
	{
		//do nothing
	}
}

package hu.elte.txtuml.export.uml2.transform.aspects;

import hu.elte.txtuml.api.ModelElement;
import hu.elte.txtuml.api.Action;
import hu.elte.txtuml.api.Collection;
import hu.elte.txtuml.export.uml2.utils.ImportWarningProvider;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.LinkedList;

import org.aspectj.lang.annotation.SuppressAjWarnings;

/**
 * This aspect contains advices supporting model import.
 * @author Adam Ancsin
 *
 */
public privileged aspect ImporterSupportingAspect extends AbstractImporterAspect {

	/**
	 * This advice shows an error message when a non-txtUML method is called from a method body
	 * during model import.
	 *
	 * @author Adam Ancsin
	 */
	@SuppressAjWarnings
	Object around() :
		call(* (!ModelElement+).*(..)) &&
		!call(* (java.lang..*).*(..)) &&
		!call(* (Collection+).*(..))
		&& isActive()
	{
		ImportWarningProvider.createWarning(
				"non-txtUML method call - " +
				thisJoinPoint.getSignature().getDeclaringType().getName() +
				"." +
				thisJoinPoint.getSignature().getName()
			);
		return proceed();
	}

	/**
	 * This advice hides all the synthetic methods from the result of Class.getDeclaredMethods() calls.
	 * It is needed to hide the private methods generated by AspectJ.
	 *
	 * @author Adam Ancsin
	 */
	@SuppressAjWarnings
	Method[] around(Object c) : target(c) && call(Method[] Class.getDeclaredMethods()) && withinExportUML2() {
		LinkedList<Method> methods = new LinkedList<>();
		for(Method m : proceed(c)) {
			if (!m.isSynthetic()) {
				methods.add(m);
			}
		}
		return methods.toArray(new Method[0]);
	}

	/**
	 * This advice hides all the synthetic fields from the result of Class.getDeclaredFields() calls.
	 * It is needed to hide the fields generated by the Java compiler. (e.g. this$0)
	 *
	 * @author Adam Ancsin
	 */
	@SuppressAjWarnings
	Field[] around(Object c) : target(c) && call(Field[] Class.getDeclaredFields()) && withinExportUML2() {
		LinkedList<Field> fields = new LinkedList<>();
		for(Field f : proceed(c)) {
			if (!f.isSynthetic()) {
				fields.add(f);
			}
		}
		return fields.toArray(new Field[0]);
	}

	/**
	 * This advice prevents execution of Action.start if called from outside the model (e.g. from glue code)
	 * during model import, so none of the ModelClass instances start functioning.
	 *
	 * @author Adam Ancsin
	 */
	@SuppressAjWarnings
	void around(): call(void Action.start(..)) && importing() && !withinModel()
	{
		//do nothing
	}

	/**
	 * This advice prevents logging in models during model import.
	 *
	 * @author Adam Ancsin
	 */
	@SuppressAjWarnings
	void around():
		(
			call(void Action.log(..)) ||
			call(void Action.logError(..)) ||
			call(void Action.executorLog(..)) ||
			call(void Action.executorFormattedLog(..)) ||
			call(void Action.executorErrorLog(..))
		)
		&& isActive()
	{
		//do nothing
	}
}


don't add collections if the journaller thread didn't start

package com.mebigfatguy.hashshmash;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public aspect HashCollector {

    private static final Journaller journaller = new Journaller();

    after() returning (Map m): call(HashMap.new(..)) {
        String fileName = thisJoinPointStaticPart.getSourceLocation().getFileName();
        int line = thisJoinPointStaticPart.getSourceLocation().getLine();

        HashMapDetails details = new HashMapDetails(m, new Date(), fileName + ":" + line);
        journaller.add(details);
    }

    after() returning (Set s): call(HashSet.new(..)) {
        String fileName = thisJoinPointStaticPart.getSourceLocation().getFileName();
        int line = thisJoinPointStaticPart.getSourceLocation().getLine();

        HashSetDetails details = new HashSetDetails(s, new Date(), fileName + ":" + line);
        journaller.add(details);
    }

    before(): execution(public static void *.main(String[])) {
        Class<?> c = HashCollector.class;
    }
}

final class Journaller implements Runnable {

    private static final int SLEEP_TIME = 5 * 1000;

    private Field tableField;
    private ConcurrentHashMap<HashDetails, HashDetails> hDetails = new ConcurrentHashMap<HashDetails, HashDetails>();
    private PrintWriter writer;
    private boolean operational = false;

    public Journaller() {
        try {
            System.out.println("*************************");
            System.out.println("HASHSHMASH ASPECT ENABLED");
            System.out.println("*************************");

            tableField = HashMap.class.getDeclaredField("table");
            tableField.setAccessible(true);

            File dir = new File(System.getProperty("user.home"), ".hashshmash");
            dir.mkdirs();

            writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(dir, new Date().toString() + ".txt")))));

            Thread t = new Thread(this);
            t.setDaemon(true);
            t.start();
        } catch (Exception e) {
            operational = true;
        }
    }

    public void add(HashDetails details) {
        if (operational)
            hDetails.put(details,  details);
    }

    public void run() {
        try {
            while (!Thread.interrupted()) {
                Thread.sleep(SLEEP_TIME);

                for (HashDetails details : hDetails.keySet()) {
                    try {
                        Map<?, ?> map = details.getMap();
                        if (map != null) {
                            int newSize = details.getMap().size();
                            if (newSize == details.size) {
                                Entry<?, ?>[] table = (Entry<?, ?>[])tableField.get(map);
                                details.totalSlots = table.length;
                                for (Entry<?, ?> e : table) {
                                    if (e != null) {
                                        details.usedSlots++;
                                    }
                                }
                                writer.println(details);
                                hDetails.remove(details);
                            } else
                                details.size = newSize;
                        } else {
                            hDetails.remove(details);
                        }
                    } catch (Exception e) {
                        //might get ConcurrentModificationException or such, just ignore
                    }
                }
                writer.flush();
            }
        } catch (InterruptedException ie) {
        }
    }
}

abstract class HashDetails {
    private static SimpleDateFormat FORMATTER = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");

    public Date allocTime;
    public String caller;
    public int size;
    public int usedSlots;
    public int totalSlots;

    public abstract Map<?,?> getMap();

    public abstract String getDetailType();

    public String toString() {
        return getDetailType() + "\t" + FORMATTER.format(allocTime) + "\t" + caller + "\t" + getMap().size() + "\t" + totalSlots + "\t" + usedSlots;
    }
}

class HashMapDetails extends HashDetails {
    public Map<?, ?> map;

    public HashMapDetails(Map<?, ?> m, Date d, String c) {
        map = m;
        allocTime = d;
        caller = c;
        size = map.size();
    }

    public Map<?,?> getMap() {
        return map;
    }

    public String getDetailType() {
        return map.getClass().getSimpleName();
    }
}

class HashSetDetails extends HashDetails {
    private static Field MAP_FIELD;
    static {
        try {
            MAP_FIELD = HashSet.class.getDeclaredField("map");
            MAP_FIELD.setAccessible(true);
        } catch (Exception e) {
            MAP_FIELD = null;
        }
    }

    public Set<?> set;

    public HashSetDetails(Set<?> s, Date d, String c) {
        set = s;
        allocTime = d;
        caller = c;
        size = set.size();
    }

    public Map<?,?> getMap() {
        try {
            if (MAP_FIELD == null)
                return null;

            return (Map<?,?>) MAP_FIELD.get(set);
        } catch (Exception e) {
            return null;
        }
    }

    public String getDetailType() {
        return set.getClass().getSimpleName();
    }
}

package com.mebigfatguy.hashshmash;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public aspect HashCollector {

    private static final Journaller journaller = new Journaller();

    after() returning (Map m): call(HashMap.new(..)) {
        String fileName = thisJoinPointStaticPart.getSourceLocation().getFileName();
        int line = thisJoinPointStaticPart.getSourceLocation().getLine();

        HashMapDetails details = new HashMapDetails(m, new Date(), fileName + ":" + line);
        journaller.add(details);
    }

    after() returning (Set s): call(HashSet.new(..)) {
        String fileName = thisJoinPointStaticPart.getSourceLocation().getFileName();
        int line = thisJoinPointStaticPart.getSourceLocation().getLine();

        HashSetDetails details = new HashSetDetails(s, new Date(), fileName + ":" + line);
        journaller.add(details);
    }

    before(): execution(public static void *.main(String[])) {
        Class<?> c = HashCollector.class;
    }
}

final class Journaller implements Runnable {

    private static final int SLEEP_TIME = 5 * 1000;

    private Field tableField;
    private ConcurrentHashMap<HashDetails, HashDetails> hDetails = new ConcurrentHashMap<HashDetails, HashDetails>();
    private PrintWriter writer;

    public Journaller() {
        try {
            tableField = HashMap.class.getDeclaredField("table");
            tableField.setAccessible(true);

            File dir = new File(System.getProperty("user.home"), ".hashshmash");
            dir.mkdirs();

            writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(dir, new Date().toString() + ".txt")))));

            Thread t = new Thread(this);
            t.setDaemon(true);
            t.start();
        } catch (Exception e) {
        }
    }

    public void add(HashDetails details) {
        hDetails.put(details,  details);
    }

    public void run() {
        try {
            while (!Thread.interrupted()) {
                Thread.sleep(SLEEP_TIME);

                for (HashDetails details : hDetails.keySet()) {
                    try {
                        Map<?, ?> map = details.getMap();
                        if (map != null) {
                            int newSize = details.getMap().size();
                            if (newSize == details.size) {
                                Entry<?, ?>[] table = (Entry<?, ?>[])tableField.get(map);
                                details.totalSlots = table.length;
                                for (Entry<?, ?> e : table) {
                                    if (e != null) {
                                        details.usedSlots++;
                                    }
                                }
                                writer.println(details);
                                hDetails.remove(details);
                            } else
                                details.size = newSize;
                        } else {
                            hDetails.remove(details);
                        }
                    } catch (Exception e) {
                        //might get ConcurrentModificationException or such, just ignore
                    }
                }
                writer.flush();
            }
        } catch (InterruptedException ie) {
        }
    }
}

abstract class HashDetails {
    private static SimpleDateFormat FORMATTER = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");

    public Date allocTime;
    public String caller;
    public int size;
    public int usedSlots;
    public int totalSlots;

    public abstract Map<?,?> getMap();

    public abstract String getDetailType();

    public String toString() {
        return getDetailType() + "\t" + FORMATTER.format(allocTime) + "\t" + caller + "\t" + getMap().size() + "\t" + totalSlots + "\t" + usedSlots;
    }
}

class HashMapDetails extends HashDetails {
    public Map<?, ?> map;

    public HashMapDetails(Map<?, ?> m, Date d, String c) {
        map = m;
        allocTime = d;
        caller = c;
        size = map.size();
    }

    public Map<?,?> getMap() {
        return map;
    }

    public String getDetailType() {
        return map.getClass().getSimpleName();
    }
}

class HashSetDetails extends HashDetails {
    private static Field MAP_FIELD;
    static {
        try {
            MAP_FIELD = HashSet.class.getDeclaredField("map");
            MAP_FIELD.setAccessible(true);
        } catch (Exception e) {
            MAP_FIELD = null;
        }
    }

    public Set<?> set;

    public HashSetDetails(Set<?> s, Date d, String c) {
        set = s;
        allocTime = d;
        caller = c;
        size = set.size();
    }

    public Map<?,?> getMap() {
        try {
            if (MAP_FIELD == null)
                return null;

            return (Map<?,?>) MAP_FIELD.get(set);
        } catch (Exception e) {
            return null;
        }
    }

    public String getDetailType() {
        return set.getClass().getSimpleName();
    }
}


Fixed #3034. Review by community.

Fixed #3034. Review by community.

git-svn-id: f4ecfe233223879a4a16cd7937f11b5152963d8e@20893 5e8d7ff9-d8ef-0310-90f0-a4852d11357a

/*
 * Copyright 2005-2010 LAMP/EPFL
 */
// $Id$

package scala.tools.eclipse.contribution.weaving.jdt.core;

import org.eclipse.jface.viewers.IStructuredSelection;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.resources.IResource;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTester;
import org.eclipse.jdt.internal.corext.refactoring.reorg.ReorgUtils;
import org.eclipse.jdt.internal.ui.refactoring.actions.RenameResourceAction;
import org.eclipse.jface.viewers.IStructuredSelection;

import scala.tools.eclipse.contribution.weaving.jdt.IScalaElement;

@SuppressWarnings("restriction")
public aspect RefactoringAvailabilityAspect {
  pointcut isAvailable(Object arg) :
    execution(static boolean RefactoringAvailabilityTester.is*Available(!IResource)) &&
    args(arg);

  pointcut isAvailable2(IResource[] arg1, IJavaElement[] arg2) :
    execution(static boolean RefactoringAvailabilityTester.is*Available(IResource[], IJavaElement[])) &&
    args(arg1, arg2);

  pointcut getResources(Object[] elements) :
    execution(static IResource[] RefactoringAvailabilityTester.getResources(Object[])) &&
    args(elements);

  pointcut getJavaElements(Object[] elements) :
    execution(static IJavaElement[] RefactoringAvailabilityTester.getJavaElements(Object[])) &&
    args(elements);

  pointcut getResources2(List elements) :
    execution(static IResource[] ReorgUtils.getResources(List)) &&
    args(elements);

  pointcut getJavaElements2(List elements) :
    execution(static IJavaElement[] ReorgUtils.getJavaElements(List)) &&
    args(elements);

  pointcut getResource(IStructuredSelection selection) :
    execution(static IResource RenameResourceAction.getResource(IStructuredSelection)) &&
    args(selection);

  boolean around(Object arg) :
    isAvailable(arg) {
    if (arg instanceof IScalaElement)
      return false;
    else if (arg instanceof IStructuredSelection)
      for(Iterator i = ((IStructuredSelection)arg).iterator(); i.hasNext();)
        if (i.next() instanceof IScalaElement)
          return false;

    return proceed(arg);
  }

  boolean around(IResource[] resources, IJavaElement[] elements) :
    isAvailable2(resources, elements) {
    if (elements == null || elements.length == 0)
      return proceed(resources, elements);

    int numElements = elements.length;
    boolean hasScalaElement = false;
    for (int i = 0; i < numElements; ++i)
      if (elements[i] instanceof IScalaElement) {
        hasScalaElement = true;
        break;
      }

    if (!hasScalaElement)
      return proceed(resources, elements);

    List newElements = new ArrayList();

    List newResources = new ArrayList();
    if (resources != null)
      newResources.addAll(Arrays.asList(resources));

    for (int i = 0; i < numElements; ++i) {
      IJavaElement elem = elements[i];
      if (elem instanceof IScalaElement) {
        try {
          IResource resource = elem.getCorrespondingResource();
          if (resource != null)
            newResources.add(resource);
        } catch (JavaModelException ex) {
          // Deliberately ignored
        }
      } else
        newElements.add(elem);
    }

    return proceed(
      (IResource[])newResources.toArray(new IResource[newResources.size()]),
      (IJavaElement[])newElements.toArray(new IJavaElement[newElements.size()]));
  }

  IResource[] around(Object[] elements) :
    getResources(elements) {
    List result= new ArrayList();
    for (int index= 0; index < elements.length; index++) {
      Object elem = elements[index];
      if (elem instanceof IResource)
        result.add(elem);
      else if (elem instanceof IScalaElement) {
        try {
          IResource resource = ((IJavaElement)elem).getCorrespondingResource();
          if (resource != null)
            result.add(resource);
        } catch (JavaModelException ex) {
          // Deliberately ignored
        }
      }
    }
    return (IResource[]) result.toArray(new IResource[result.size()]);
  }

  IJavaElement[] around(Object[] elements) :
    getJavaElements(elements) {
    List result= new ArrayList();
    for (int index= 0; index < elements.length; index++) {
      Object elem = elements[index];
      if ((elem instanceof IJavaElement) && !(elem instanceof IScalaElement))
        result.add(elem);
    }
    return (IJavaElement[]) result.toArray(new IJavaElement[result.size()]);
  }

  IResource[] around(List elements) :
    getResources2(elements) {
    List result= new ArrayList();
    for (Iterator i = elements.iterator(); i.hasNext(); ) {
      Object elem = i.next();
      if (elem instanceof IResource)
        result.add(elem);
      else if (elem instanceof IScalaElement) {
        try {
          IResource resource = ((IJavaElement)elem).getCorrespondingResource();
          if (resource != null)
            result.add(resource);
        } catch (JavaModelException ex) {
          // Deliberately ignored
        }
      }
    }
    return (IResource[]) result.toArray(new IResource[result.size()]);
  }

  IJavaElement[] around(List elements) :
    getJavaElements2(elements) {
    List result= new ArrayList();
    for (Iterator i = elements.iterator(); i.hasNext(); ) {
      Object elem = i.next();
      if ((elem instanceof IJavaElement) && !(elem instanceof IScalaElement))
        result.add(elem);
    }
    return (IJavaElement[]) result.toArray(new IJavaElement[result.size()]);
  }

  IResource around(IStructuredSelection selection) :
    getResource(selection) {
    if (selection.size() != 1)
      return null;
    Object first= selection.getFirstElement();
    if (first instanceof IScalaElement) {
      try {
        return ((IJavaElement)first).getCorrespondingResource();
      } catch (JavaModelException ex) {
        return null;
      }
    }
    else if (!(first instanceof IResource))
      return null;
    return (IResource)first;
  }
}

/*
 * Copyright 2005-2010 LAMP/EPFL
 */
// $Id$

package scala.tools.eclipse.contribution.weaving.jdt.core;

import org.eclipse.jface.viewers.IStructuredSelection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.resources.IResource;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTester;
import org.eclipse.jdt.internal.corext.refactoring.reorg.ReorgUtils;
import org.eclipse.jdt.internal.ui.refactoring.actions.RenameResourceAction;
import org.eclipse.jface.viewers.IStructuredSelection;

import scala.tools.eclipse.contribution.weaving.jdt.IScalaElement;

@SuppressWarnings("restriction")
public aspect RefactoringAvailabilityAspect {
  pointcut isAvailable(Object arg) :
    execution(static boolean RefactoringAvailabilityTester.is*Available(!IResource)) &&
    args(arg);

  pointcut isAvailable2(IResource[] arg1, IJavaElement[] arg2) :
    execution(static boolean RefactoringAvailabilityTester.is*Available(IResource[], IJavaElement[])) &&
    args(arg1, arg2);

  pointcut getResources(Object[] elements) :
    execution(static IResource[] RefactoringAvailabilityTester.getResources(Object[])) &&
    args(elements);

  pointcut getJavaElements(Object[] elements) :
    execution(static IJavaElement[] RefactoringAvailabilityTester.getJavaElements(Object[])) &&
    args(elements);

  pointcut getResources2(List elements) :
    execution(static IResource[] ReorgUtils.getResources(List)) &&
    args(elements);

  pointcut getJavaElements2(List elements) :
    execution(static IJavaElement[] ReorgUtils.getJavaElements(List)) &&
    args(elements);

  pointcut getResource(IStructuredSelection selection) :
    execution(static IResource RenameResourceAction.getResource(IStructuredSelection)) &&
    args(selection);

  boolean around(Object arg) :
    isAvailable(arg) {
    if (arg instanceof IScalaElement)
      return false;
    else if (arg instanceof IStructuredSelection)
      for(Iterator i = ((IStructuredSelection)arg).iterator(); i.hasNext();)
        if (i.next() instanceof IScalaElement)
          return false;

    return proceed(arg);
  }

  boolean around(IResource[] resources, IJavaElement[] elements) :
    isAvailable2(resources, elements) {
    if (elements == null || elements.length == 0)
      return proceed(resources, elements);
    else if (resources == null || resources.length == 0) {
      if (elements == null || elements.length == 0)
        return false;

      int limit = elements.length;
      IResource[] newResources = new IResource[limit];
      for (int i = 0; i < limit; ++i) {
        IJavaElement elem = elements[i];
        if (elem instanceof IResource)
          newResources[i] = (IResource)elem;
        else
          return false;
      }
      return proceed(newResources, null);
    }
    else {
      int limit = elements.length;
      IResource[] newResources = new IResource[resources.length+limit];
      System.arraycopy(resources, 0, newResources, 0, resources.length);
      for (int i = 0, j = resources.length; i < limit; ++i, ++j) {
        IJavaElement elem = elements[i];
        if (elem instanceof IResource)
          newResources[j] = (IResource)elem;
        else
          return false;
      }
      return proceed(newResources, null);
    }
  }

  IResource[] around(Object[] elements) :
    getResources(elements) {
    List result= new ArrayList();
    for (int index= 0; index < elements.length; index++) {
      Object elem = elements[index];
      if (elem instanceof IResource)
        result.add(elem);
      else if (elem instanceof IScalaElement) {
        try {
          IResource resource = ((IJavaElement)elem).getCorrespondingResource();
          if (resource != null)
            result.add(resource);
        } catch (JavaModelException ex) {
          // Deliberately ignored
        }
      }
    }
    return (IResource[]) result.toArray(new IResource[result.size()]);
  }

  IJavaElement[] around(Object[] elements) :
    getJavaElements(elements) {
    List result= new ArrayList();
    for (int index= 0; index < elements.length; index++) {
      Object elem = elements[index];
      if ((elem instanceof IJavaElement) && !(elem instanceof IScalaElement))
        result.add(elem);
    }
    return (IJavaElement[]) result.toArray(new IJavaElement[result.size()]);
  }

  IResource[] around(List elements) :
    getResources2(elements) {
    List result= new ArrayList();
    for (Iterator i = elements.iterator(); i.hasNext(); ) {
      Object elem = i.next();
      if (elem instanceof IResource)
        result.add(elem);
      else if (elem instanceof IScalaElement) {
        try {
          IResource resource = ((IJavaElement)elem).getCorrespondingResource();
          if (resource != null)
            result.add(resource);
        } catch (JavaModelException ex) {
          // Deliberately ignored
        }
      }
    }
    return (IResource[]) result.toArray(new IResource[result.size()]);
  }

  IJavaElement[] around(List elements) :
    getJavaElements2(elements) {
    List result= new ArrayList();
    for (Iterator i = elements.iterator(); i.hasNext(); ) {
      Object elem = i.next();
      if ((elem instanceof IJavaElement) && !(elem instanceof IScalaElement))
        result.add(elem);
    }
    return (IJavaElement[]) result.toArray(new IJavaElement[result.size()]);
  }

  IResource around(IStructuredSelection selection) :
    getResource(selection) {
    if (selection.size() != 1)
      return null;
    Object first= selection.getFirstElement();
    if (first instanceof IScalaElement) {
      try {
        return ((IJavaElement)first).getCorrespondingResource();
      } catch (JavaModelException ex) {
        return null;
      }
    }
    else if (!(first instanceof IResource))
      return null;
    return (IResource)first;
  }
}


Relationship backing aspect now also works in unitialized case

/*
 * Copyright 2010 the original author or authors.
 *
 * 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.springframework.data.graph.neo4j.support.relationship;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.FieldSignature;
import org.neo4j.graphdb.Relationship;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.data.graph.core.RelationshipBacked;
import org.springframework.data.graph.neo4j.fieldaccess.*;
import org.springframework.data.graph.neo4j.support.GraphDatabaseContext;
import org.springframework.data.graph.annotation.*;

import java.lang.reflect.Field;

import static org.springframework.data.graph.neo4j.fieldaccess.DoReturn.unwrap;

/**
 * Aspect for handling relationship entity creation and field access (read & write)
 * puts the underlying state into and delegates field access to an {@link org.springframework.data.graph.neo4j.fieldaccess.EntityState} instance,
 * created by a configured {@link org.springframework.data.graph.neo4j.fieldaccess.RelationshipEntityStateFactory}
 */
public aspect Neo4jRelationshipBacking {

    protected final Log log = LogFactory.getLog(getClass());

    declare parents : (@RelationshipEntity *) implements RelationshipBacked;
    declare @type: RelationshipBacked+: @Configurable;

    protected pointcut entityFieldGet(RelationshipBacked entity) :
            get(* RelationshipBacked+.*) &&
            this(entity) &&
            !get(* RelationshipBacked.*);

    protected pointcut entityFieldSet(RelationshipBacked entity, Object newVal) :
            set(* RelationshipBacked+.*) &&
            this(entity) &&
            args(newVal) &&
            !set(* RelationshipBacked.*);

	private GraphDatabaseContext graphDatabaseContext;
    private RelationshipEntityStateFactory entityStateFactory;

    public void setGraphDatabaseContext(GraphDatabaseContext graphDatabaseContext) {
        this.graphDatabaseContext = graphDatabaseContext;
    }

    public void setRelationshipEntityStateFactory(RelationshipEntityStateFactory entityStateFactory) {
        this.entityStateFactory = entityStateFactory;
    }

    /**
     * field for {@link org.springframework.data.graph.neo4j.fieldaccess.EntityState} that takes care of all entity operations
     */
    private EntityState<RelationshipBacked,Relationship> RelationshipBacked.entityState;

    /**
     * creates a new {@link org.springframework.data.graph.neo4j.fieldaccess.EntityState} instance with the relationship parameter or updates an existing one
     * @param r
     */
	public void RelationshipBacked.setPersistentState(Relationship r) {
        if (this.entityState == null) {
            this.entityState = Neo4jRelationshipBacking.aspectOf().entityStateFactory.getEntityState(this);
        }
        this.entityState.setPersistentState(r);
	}

	public Relationship RelationshipBacked.getPersistentState() {
		return this.entityState.getPersistentState();
	}

	public boolean RelationshipBacked.hasUnderlyingRelationship() {
		return this.entityState!=null && this.entityState.hasPersistentState();
	}

    /**
     * @return relationship id if there is an underlying relationship
     */
	public Long RelationshipBacked.getRelationshipId() {
        if (!hasUnderlyingRelationship()) return null;
		return getPersistentState().getId();
	}

    /**
     * @param obj
     * @return result of equality check of the underlying relationship
     */
	public final boolean RelationshipBacked.equals(Object obj) {
		if (obj instanceof RelationshipBacked) {
			return this.getPersistentState().equals(((RelationshipBacked) obj).getPersistentState());
		}
		return false;
	}

    /**
     * @return hashCode of the underlying relationship
     */
	public final int RelationshipBacked.hashCode() {
		return getPersistentState().hashCode();
	}

	public void RelationshipBacked.remove() {
	     Neo4jRelationshipBacking.aspectOf().graphDatabaseContext.removeRelationshipEntity(this);
	}

    public <R extends RelationshipBacked> R  RelationshipBacked.projectTo(Class<R> targetType) {
        return (R)Neo4jRelationshipBacking.aspectOf().graphDatabaseContext.projectTo(this, targetType);
    }

    Object around(RelationshipBacked entity): entityFieldGet(entity) {
        if (entity.entityState == null) return proceed(entity);
        Object result = entity.entityState.getValue(field(thisJoinPoint));
        if (result instanceof DoReturn) return unwrap(result);
        return proceed(entity);
    }

    Object around(RelationshipBacked entity, Object newVal) : entityFieldSet(entity, newVal) {
        if (entity.entityState == null) return proceed(entity,newVal);
        Object result=entity.entityState.setValue(field(thisJoinPoint),newVal);
        if (result instanceof DoReturn) return unwrap(result);
        return proceed(entity,result);
	}

    Field field(JoinPoint joinPoint) {
        FieldSignature fieldSignature = (FieldSignature)joinPoint.getSignature();
        return fieldSignature.getField();
    }
}

/*
 * Copyright 2010 the original author or authors.
 *
 * 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.springframework.data.graph.neo4j.support.relationship;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.FieldSignature;
import org.neo4j.graphdb.Relationship;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.data.graph.core.RelationshipBacked;
import org.springframework.data.graph.neo4j.fieldaccess.*;
import org.springframework.data.graph.neo4j.support.GraphDatabaseContext;
import org.springframework.data.graph.annotation.*;

import java.lang.reflect.Field;

import static org.springframework.data.graph.neo4j.fieldaccess.DoReturn.unwrap;

/**
 * Aspect for handling relationship entity creation and field access (read & write)
 * puts the underlying state into and delegates field access to an {@link org.springframework.data.graph.neo4j.fieldaccess.EntityState} instance,
 * created by a configured {@link org.springframework.data.graph.neo4j.fieldaccess.RelationshipEntityStateFactory}
 */
public aspect Neo4jRelationshipBacking {

    protected final Log log = LogFactory.getLog(getClass());

    declare parents : (@RelationshipEntity *) implements RelationshipBacked;
    declare @type: RelationshipBacked+: @Configurable;

    protected pointcut entityFieldGet(RelationshipBacked entity) :
            get(* RelationshipBacked+.*) &&
            this(entity) &&
            !get(* RelationshipBacked.*);

    protected pointcut entityFieldSet(RelationshipBacked entity, Object newVal) :
            set(* RelationshipBacked+.*) &&
            this(entity) &&
            args(newVal) &&
            !set(* RelationshipBacked.*);

	private GraphDatabaseContext graphDatabaseContext;
    private RelationshipEntityStateFactory entityStateFactory;

    public void setGraphDatabaseContext(GraphDatabaseContext graphDatabaseContext) {
        this.graphDatabaseContext = graphDatabaseContext;
    }

    public void setRelationshipEntityStateFactory(RelationshipEntityStateFactory entityStateFactory) {
        this.entityStateFactory = entityStateFactory;
    }

    /**
     * field for {@link org.springframework.data.graph.neo4j.fieldaccess.EntityState} that takes care of all entity operations
     */
    private EntityState<RelationshipBacked,Relationship> RelationshipBacked.entityState;

    /**
     * creates a new {@link org.springframework.data.graph.neo4j.fieldaccess.EntityState} instance with the relationship parameter or updates an existing one
     * @param r
     */
	public void RelationshipBacked.setPersistentState(Relationship r) {
        if (this.entityState == null) {
            this.entityState = Neo4jRelationshipBacking.aspectOf().entityStateFactory.getEntityState(this);
        }
        this.entityState.setPersistentState(r);
	}

	public Relationship RelationshipBacked.getPersistentState() {
		return this.entityState.getPersistentState();
	}

	public boolean RelationshipBacked.hasUnderlyingRelationship() {
		return this.entityState.hasPersistentState();
	}

    /**
     * @return relationship id if there is an underlying relationship
     */
	public Long RelationshipBacked.getRelationshipId() {
        if (!hasUnderlyingRelationship()) return null;
		return getPersistentState().getId();
	}

    /**
     * @param obj
     * @return result of equality check of the underlying relationship
     */
	public final boolean RelationshipBacked.equals(Object obj) {
		if (obj instanceof RelationshipBacked) {
			return this.getPersistentState().equals(((RelationshipBacked) obj).getPersistentState());
		}
		return false;
	}

    /**
     * @return hashCode of the underlying relationship
     */
	public final int RelationshipBacked.hashCode() {
		return getPersistentState().hashCode();
	}

	public void RelationshipBacked.remove() {
	     Neo4jRelationshipBacking.aspectOf().graphDatabaseContext.removeRelationshipEntity(this);
	}

    public <R extends RelationshipBacked> R  RelationshipBacked.projectTo(Class<R> targetType) {
        return (R)Neo4jRelationshipBacking.aspectOf().graphDatabaseContext.projectTo(this, targetType);
    }

    Object around(RelationshipBacked entity): entityFieldGet(entity) {
        Object result=entity.entityState.getValue(field(thisJoinPoint));
        if (result instanceof DoReturn) return unwrap(result);
        return proceed(entity);
    }

    Object around(RelationshipBacked entity, Object newVal) : entityFieldSet(entity, newVal) {
        Object result=entity.entityState.setValue(field(thisJoinPoint),newVal);
        if (result instanceof DoReturn) return unwrap(result);
        return proceed(entity,result);
	}

    Field field(JoinPoint joinPoint) {
        FieldSignature fieldSignature = (FieldSignature)joinPoint.getSignature();
        return fieldSignature.getField();
    }
}


formatting

formatting

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.LinearGradientPaint;

import javax.swing.JPanel;
import javax.swing.JTabbedPane;

import model.SystemValues.Games;
import model.SystemValues.Players;
import view.GUI;
import view.UIUpdater;
import de.erichseifert.gral.data.DataTable;
import de.erichseifert.gral.plots.BarPlot;
import de.erichseifert.gral.plots.BarPlot.BarRenderer;
import de.erichseifert.gral.plots.PiePlot;
import de.erichseifert.gral.plots.PiePlot.PieSliceRenderer;
import de.erichseifert.gral.plots.points.PointRenderer;
import de.erichseifert.gral.ui.InteractivePanel;
import de.erichseifert.gral.util.GraphicsUtils;
import de.erichseifert.gral.util.Insets2D;
import de.erichseifert.gral.util.Location;

public privileged aspect StatPlayerWiseGraph {
    /**
     * Statistics has to be the last feature executed, such that the subfeatures
     * have the correct gui object before statistics can create the tabbed pane
     */
    declare precedence : Statistics, StatPlayerWiseGraph, StatPlayerWiseTxt, StatOverallGraph, StatOverallTxt;

    private static GUI gui;

    private static final JTabbedPane playersTabs = new JTabbedPane();
    private static final Color COLOR1 = Color.GREEN;

    private static DataTable playedTypes[] = new DataTable[4];
    private static DataTable wonGames[] = new DataTable[4];

    after() returning(GUI gui): call(GUI.new(*)) {
        StatPlayerWiseGraph.gui = gui;
    }

    @SuppressWarnings("unchecked")
    after(JTabbedPane tabbedPane):
                        execution(public static void StatisticsHelper.StatisticsPaneCreated(JTabbedPane))
                        && args(tabbedPane) {

        for (int i = 0; i < Players.values().length; i++) {
            playedTypes[i] = new DataTable(Integer.class, String.class);
            wonGames[i] = new DataTable(Integer.class, Double.class,String.class);
        }

        updateData();
;
        for (Players player : Players.values()) {
            playersTabs.addTab(player.toString(), createPlayersPanel(player));
        }
        tabbedPane.addTab("Einzelstatistiken", playersTabs);
    }

    private JPanel createPlayersPanel(Players player) {
        JPanel containerPanel = new JPanel();
        containerPanel.setLayout(new GridLayout(2, 2));

        containerPanel.add(new InteractivePanel(createWonGamesBarPlot(player)));
        containerPanel.add(new InteractivePanel(createPlayedTypesPlot(player)));
        return containerPanel;
    }

    after() : execution(* UIUpdater.run()) {
        updateData();
        for (Players player : Players.values()) {
            playersTabs.setTitleAt(player.ordinal(), player.toString());
        }
    }

    private static void updateData() {
        for (Players player : Players.values()) {
            PlayerWiseStatistics data = gui.model.getGameData().getPlayerWiseStatistics(player);

            int curPlayer = player.ordinal();

            while (playedTypes[curPlayer].getRowCount() > 0) {
                playedTypes[curPlayer].removeLast();
            }
            playedTypes[curPlayer].add(data.getGamesPlayedPerType()[Games.RUFSPIEL.ordinal()], "Rufspiele");
            playedTypes[curPlayer].add(data.getGamesPlayedPerType()[Games.SOLO.ordinal()], "Soli");
            playedTypes[curPlayer].add(data.getGamesPlayedPerType()[Games.TOUT.ordinal()], "Soli Tout");
            playedTypes[curPlayer].add(data.getGamesPlayedPerType()[Games.SIE.ordinal()],"Soli Sie");
            playedTypes[curPlayer].add(data.getGamesPlayedPerType()[Games.WEITER.ordinal()], "Weiter");

            // percentage of won games by gametype
            int soliCompleteAmount = data.getGamesPlayedPerType()[Games.SOLO.ordinal()]
                    + data.getGamesPlayedPerType()[Games.SIE.ordinal()]
                    + data.getGamesPlayedPerType()[Games.TOUT.ordinal()];
            int soliCompleteAmountWon = data.getGamesWonPerType()[Games.SOLO.ordinal()]
                    + data.getGamesWonPerType()[Games.SIE.ordinal()]
                    + data.getGamesWonPerType()[Games.TOUT.ordinal()];
            double soliGesamtWonPerc = soliCompleteAmount > 0
                    ? (double) soliCompleteAmountWon / soliCompleteAmount
                    : 0.0;

            while (wonGames[curPlayer].getRowCount() > 0) {
                wonGames[curPlayer].removeLast();
            }
            wonGames[curPlayer].add(1, data.getGamesWonPerc(), "Gesamt");
            wonGames[curPlayer].add(2, data.getGamesWonPerTypePerc()[Games.RUFSPIEL.ordinal()], "Rufspiele");
            wonGames[curPlayer].add(3, soliGesamtWonPerc, "Soli gesamt");
            wonGames[curPlayer].add(4, data.getGamesWonPerTypePerc()[Games.SOLO.ordinal()], "Soli normal");
            wonGames[curPlayer].add(5, data.getGamesWonPerTypePerc()[Games.TOUT.ordinal()], "Soli Tout");
            wonGames[curPlayer].add(6, data.getGamesWonPerTypePerc()[Games.SIE.ordinal()], "Soli Sie");
        }
    }

    private static PiePlot createPlayedTypesPlot(Players player) {
        int playerIndex = player.ordinal();

        PiePlot plot = new PiePlot(playedTypes[playerIndex]);

        PointRenderer pointRenderer = plot
                .getPointRenderer(playedTypes[playerIndex]);
        pointRenderer.setValueVisible(true);
        pointRenderer.setValueColumn(1);

        // Format plot
        plot.getTitle().setText("Anzahl gespielter Spieltypen");
        // Change plot size
        plot.setRadius(0.9);
        // Display a legend
        plot.setLegendVisible(true);
        // Add some margin to the plot area
        plot.setInsets(new Insets2D.Double(20.0, 40.0, 40.0, 40.0));

        PieSliceRenderer pieSliceRenderer = (PieSliceRenderer) plot
                .getPointRenderer(playedTypes[playerIndex]);

        // Change relative size of inner region
        pieSliceRenderer.setInnerRadius(0.4);
        // Change the width of gaps between segments
        pieSliceRenderer.setGap(0.2);
        // Show labels
        pieSliceRenderer.setValueVisible(true);
        pieSliceRenderer.setValueColor(Color.WHITE);
        pieSliceRenderer.setValueFont(Font.decode(null).deriveFont(Font.BOLD));

        return plot;
    }

    public BarPlot createWonGamesBarPlot(Players player) {
        int playerIndex = player.ordinal();

        // Create new bar plot
        BarPlot plot = new BarPlot(wonGames);

        // Format plot
        plot.getTitle().setText("Gewonnene Spiele pro Spieltyp (in %)");
        plot.setInsets(new Insets2D.Double(40.0, 40.0, 40.0, 40.0));
        plot.setBarWidth(0.075);

        // Format bars
        PointRenderer pointRenderer = plot.getPointRenderer(wonGames[playerIndex]);
        BarRenderer barRenderer = (BarRenderer) pointRenderer;

        barRenderer.setColor(new LinearGradientPaint(0f, 0f, 0f, 1f,
                new float[] { 0.0f, 1.0f }, new Color[] { COLOR1,
                        GraphicsUtils.deriveBrighter(COLOR1) }));
        barRenderer.setBorderStroke(new BasicStroke(3f));
        barRenderer.setBorderColor(new LinearGradientPaint(0f, 0f, 0f, 1f,
                new float[] { 0.0f, 1.0f }, new Color[] {
                        GraphicsUtils.deriveBrighter(COLOR1), COLOR1 }));

        pointRenderer.setValueVisible(true);
        pointRenderer.setValueColumn(2);

        pointRenderer.setValueLocation(Location.CENTER);
        pointRenderer.setValueColor(GraphicsUtils.deriveDarker(COLOR1));
        pointRenderer.setValueFont(Font.decode(null).deriveFont(Font.BOLD));

        return plot;
    }
}

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.LinearGradientPaint;

import javax.swing.JPanel;
import javax.swing.JTabbedPane;

import model.SystemValues.Games;
import model.SystemValues.Players;
import view.GUI;
import view.UIUpdater;
import de.erichseifert.gral.data.DataTable;
import de.erichseifert.gral.plots.BarPlot;
import de.erichseifert.gral.plots.BarPlot.BarRenderer;
import de.erichseifert.gral.plots.PiePlot;
import de.erichseifert.gral.plots.PiePlot.PieSliceRenderer;
import de.erichseifert.gral.plots.points.PointRenderer;
import de.erichseifert.gral.ui.InteractivePanel;
import de.erichseifert.gral.util.GraphicsUtils;
import de.erichseifert.gral.util.Insets2D;
import de.erichseifert.gral.util.Location;

public privileged aspect StatPlayerWiseGraph {
    /**
     * Statistics has to be the last feature executed, such that the subfeatures
     * have the correct gui object before statistics can create the tabbed pane
     */
    declare precedence : Statistics, StatPlayerWiseGraph, StatPlayerWiseTxt, StatOverallGraph, StatOverallTxt;

    private static GUI gui;

    private static final JTabbedPane playersTabs = new JTabbedPane();
    private static final Color COLOR1 = Color.GREEN;

    private static DataTable playedTypes[] = new DataTable[4];
    private static DataTable wonGames[] = new DataTable[4];

    after() returning(GUI gui): call(GUI.new(*)) {
        StatPlayerWiseGraph.gui = gui;
    }

    @SuppressWarnings("unchecked")
    after(JTabbedPane tabbedPane):
                        execution(public static void StatisticsHelper.StatisticsPaneCreated(JTabbedPane))
                        && args(tabbedPane) {

        for (int i = 0; i < Players.values().length; i++) {
            playedTypes[i] = new DataTable(Integer.class, String.class);
            wonGames[i] = new DataTable(Integer.class, Double.class,String.class);
        }

        updateData();
;
        for (Players player : Players.values()) {
            playersTabs.addTab(player.toString(), createPlayersPanel(player));
        }
        tabbedPane.addTab("Einzelstatistiken", playersTabs);
    }

    private JPanel createPlayersPanel(Players player) {
        JPanel containerPanel = new JPanel();
        containerPanel.setLayout(new GridLayout(2, 2));

        containerPanel.add(new InteractivePanel(createWonGamesBarPlot(player)));
        containerPanel.add(new InteractivePanel(createPlayedTypesPlot(player)));
        return containerPanel;
    }

    after() : execution(* UIUpdater.run()) {
        updateData();
        for (Players player : Players.values()) {
            playersTabs.setTitleAt(player.ordinal(), player.toString());
        }
    }

    private static void updateData() {
        for (Players player : Players.values()) {
            PlayerWiseStatistics data = gui.model.getGameData()
                    .getPlayerWiseStatistics(player);

            int i = player.ordinal();

            while (playedTypes[i].getRowCount() > 0) {
                playedTypes[i].removeLast();
            }
            playedTypes[i].add(
                    data.getGamesPlayedPerType()[Games.RUFSPIEL.ordinal()],
                    "Rufspiele");
            playedTypes[i].add(
                    data.getGamesPlayedPerType()[Games.SOLO.ordinal()], "Soli");
            playedTypes[i].add(
                    data.getGamesPlayedPerType()[Games.TOUT.ordinal()],
                    "Soli Tout");
            playedTypes[i].add(
                    data.getGamesPlayedPerType()[Games.SIE.ordinal()],
                    "Soli Sie");
            playedTypes[i].add(
                    data.getGamesPlayedPerType()[Games.WEITER.ordinal()],
                    "Weiter");

            // percentage of won games by gametype
            int soli_complete_amount = data.getGamesPlayedPerType()[Games.SOLO
                    .ordinal()]
                    + data.getGamesPlayedPerType()[Games.SIE.ordinal()]
                    + data.getGamesPlayedPerType()[Games.TOUT.ordinal()];
            int soli_complete_amount_won = data.getGamesWonPerType()[Games.SOLO
                    .ordinal()]
                    + data.getGamesWonPerType()[Games.SIE.ordinal()]
                    + data.getGamesWonPerType()[Games.TOUT.ordinal()];
            double soli_gesamt_won_perc = soli_complete_amount > 0 ? (double) soli_complete_amount_won
                    / soli_complete_amount
                    : 0.0;

            while (wonGames[i].getRowCount() > 0) {
                wonGames[i].removeLast();
            }
            wonGames[i].add(1, data.getGamesWonPerc(), "Gesamt");
            wonGames[i].add(2,
                    data.getGamesWonPerTypePerc()[Games.RUFSPIEL.ordinal()],
                    "Rufspiele");
            wonGames[i].add(3, soli_gesamt_won_perc, "Soli gesamt");
            wonGames[i].add(4,
                    data.getGamesWonPerTypePerc()[Games.SOLO.ordinal()],
                    "Soli normal");
            wonGames[i].add(5,
                    data.getGamesWonPerTypePerc()[Games.TOUT.ordinal()],
                    "Soli Tout");
            wonGames[i].add(6,
                    data.getGamesWonPerTypePerc()[Games.SIE.ordinal()],
                    "Soli Sie");
        }
    }

    private static PiePlot createPlayedTypesPlot(Players player) {
        int playerIndex = player.ordinal();

        PiePlot plot = new PiePlot(playedTypes[playerIndex]);

        PointRenderer pointRenderer = plot
                .getPointRenderer(playedTypes[playerIndex]);
        pointRenderer.setValueVisible(true);
        pointRenderer.setValueColumn(1);

        // Format plot
        plot.getTitle().setText("Anzahl gespielter Spieltypen");
        // Change plot size
        plot.setRadius(0.9);
        // Display a legend
        plot.setLegendVisible(true);
        // Add some margin to the plot area
        plot.setInsets(new Insets2D.Double(20.0, 40.0, 40.0, 40.0));

        PieSliceRenderer pieSliceRenderer = (PieSliceRenderer) plot
                .getPointRenderer(playedTypes[playerIndex]);

        // Change relative size of inner region
        pieSliceRenderer.setInnerRadius(0.4);
        // Change the width of gaps between segments
        pieSliceRenderer.setGap(0.2);
        // Show labels
        pieSliceRenderer.setValueVisible(true);
        pieSliceRenderer.setValueColor(Color.WHITE);
        pieSliceRenderer.setValueFont(Font.decode(null).deriveFont(Font.BOLD));

        return plot;
    }

    public BarPlot createWonGamesBarPlot(Players player) {
        int playerIndex = player.ordinal();

        // Create new bar plot
        BarPlot plot = new BarPlot(wonGames);

        // Format plot
        plot.getTitle().setText("Gewonnene Spiele pro Spieltyp (in %)");
        plot.setInsets(new Insets2D.Double(40.0, 40.0, 40.0, 40.0));
        plot.setBarWidth(0.075);

        // Format bars
        PointRenderer pointRenderer = plot
                .getPointRenderer(wonGames[playerIndex]);
        BarRenderer barRenderer = (BarRenderer) pointRenderer;

        barRenderer.setColor(new LinearGradientPaint(0f, 0f, 0f, 1f,
                new float[] { 0.0f, 1.0f }, new Color[] { COLOR1,
                        GraphicsUtils.deriveBrighter(COLOR1) }));
        barRenderer.setBorderStroke(new BasicStroke(3f));
        barRenderer.setBorderColor(new LinearGradientPaint(0f, 0f, 0f, 1f,
                new float[] { 0.0f, 1.0f }, new Color[] {
                        GraphicsUtils.deriveBrighter(COLOR1), COLOR1 }));

        pointRenderer.setValueVisible(true);
        pointRenderer.setValueColumn(2);

        pointRenderer.setValueLocation(Location.CENTER);
        pointRenderer.setValueColor(GraphicsUtils.deriveDarker(COLOR1));
        pointRenderer.setValueFont(Font.decode(null).deriveFont(Font.BOLD));

        return plot;
    }
}


Context may be null...

Context may be null...


initialise maps at declaration

initialise maps at declaration

/*
 * Copyright (c) 2013 original authors
 *
 * 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.eigengo.monitor.agent.akka;

import akka.actor.*;
import org.eigengo.monitor.agent.AgentConfiguration;
import org.eigengo.monitor.output.CounterInterface;
import scala.Option;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Contains advices for monitoring behaviour of an actor; typically imprisoned in an {@code ActorCell}.
 */
public aspect ActorCellMonitoringAspect extends AbstractMonitoringAspect issingleton() {
    private AkkaAgentConfiguration agentConfiguration;
    private final CounterInterface counterInterface;
    private final Option<String> noActorClazz = Option.empty();
    private final Map<Option<String>, AtomicLong> samplingCounters  = new ConcurrentHashMap<Option<String>, AtomicLong>();
    private final Map<Option<String>, AtomicInteger> numberOfActors = new ConcurrentHashMap<Option<String>, AtomicInteger>();

    /**
     * Constructs this aspect
     */
    public ActorCellMonitoringAspect() {
        AgentConfiguration<AkkaAgentConfiguration> configuration = getAgentConfiguration("akka", AkkaAgentConfigurationJapi.apply());
        this.agentConfiguration = configuration.agent();
        this.counterInterface = createCounterInterface(configuration.common());
    }

    /**
     * Injects the new {@code AkkaAgentConfiguration} instance.
     *
     * @param agentConfiguration the new configuration
     */
    synchronized final void setAgentConfiguration(AkkaAgentConfiguration agentConfiguration) {
        this.agentConfiguration = agentConfiguration;
    }

    // decide whether to include this ActorCell in our measurements
    private boolean includeActorPath(final PathAndClass pathAndClass) {
        if (!this.agentConfiguration.incuded().accept(pathAndClass)) return false;
        if (this.agentConfiguration.excluded().accept(pathAndClass)) return false;

        if (this.agentConfiguration.includeSystemAgents()) return true;

        String userOrSystem = pathAndClass.actorPath().getElements().iterator().next();
        return "user".equals(userOrSystem);
    }

    // get the sample rate for an actor
    private int getSampleRate(final PathAndClass pathAndClass) {
        return this.agentConfiguration.sampling().getRate(pathAndClass);
    }

    // decide whether to sample an actor on a particular occasion
    private final boolean sampleMessage(final PathAndClass pathAndClass) {
        int sampleRate = getSampleRate(pathAndClass);
        if (sampleRate == 1) return true;

        this.samplingCounters.putIfAbsent(pathAndClass.actorClassName(), new AtomicLong(0));
        long timesSeenSoFar = this.samplingCounters.get(pathAndClass.actorClassName()).incrementAndGet();
        return (timesSeenSoFar % sampleRate == 1); // == 1 to log first value (incrementAndGet returns updated value)
    }

    // get the tags for the cell
    private String[] getTags(final ActorPath actorPath, final Actor actor) {
        List<String> tags = new ArrayList<String>();

        // TODO: Improve detection of routed actors. This relies only on the naming :(.
        String lastPathElement = actorPath.elements().last();
        if (lastPathElement.startsWith("$")) {
            // this is routed actor.
            tags.add(actorPath.parent().toString());
            if (this.agentConfiguration.includeRoutees()) tags.add(actorPath.toString());
        } else {
            // there is no supervisor
            tags.add(actorPath.toString());
        }
        if (actor != null) {
            tags.add(String.format("akka:%s.%s", actor.context().system().name(), actor.getClass().getCanonicalName()));
        }

        return tags.toArray(new String[tags.size()]);
    }

    // returns the canonical name of the actor type associated with an ActorCell
    private String uncheckedActorNameFrom(final ActorCell actorCell) {
        return uncheckedActorNameFrom(actorCell.props());
    }

    // returns the canonical name of the actor type associated with a Props instance
    private String uncheckedActorNameFrom(final Props props) {
        return props.actorClass().getCanonicalName();
    }

    /**
     * Advises the {@code ActorCell.receiveMessage(message: Object): Unit}
     * We proceed with the pointcut if the actor is to be included in the monitoring *and* this is
     * the 'multiple-of-n'th time we've seen a message for an actor with a sample rate of n.
     *
     * Currently, we sample queue size, the fact that the message is delivered, the simple name of the class of the
     * message, and the time taken to complete the actor's reactive action.
     *
     * @param actorCell the ActorCell where the actor that receives the message "lives"
     * @param msg the incoming message
     */
    Object around(ActorCell actorCell, Object msg) : Pointcuts.actorCellReceiveMessage(actorCell, msg) {
        final ActorPath actorPath = actorCell.self().path();
        final PathAndClass pathAndClass = new PathAndClass(actorPath, Option.apply(uncheckedActorNameFrom(actorCell)));
        if (!includeActorPath(pathAndClass) || !sampleMessage(pathAndClass)) return proceed(actorCell, msg);

        int samplingRate = getSampleRate(pathAndClass);

        // we tag by actor name
        final String[] tags = getTags(actorPath, actorCell.actor());

        // record the queue size
        this.counterInterface.recordGaugeValue("akka.queue.size", actorCell.numberOfMessages(), tags);
        // record the message, general and specific
        this.counterInterface.incrementCounter("akka.actor.delivered", samplingRate, tags);
        this.counterInterface.incrementCounter("akka.actor.delivered." + msg.getClass().getSimpleName(), samplingRate, tags);

        // measure the time. we're using the ``nanoTime`` call to access the high-precision timer.
        // since we're not really interested in wall time, but just some increasing measure of
        // elapsed time
        Object result = null;
        final long start = System.nanoTime();
        // result will always be ``null``, because target returns ``Unit``
        result = proceed(actorCell, msg);
        // ns or µs is too fine. ms will do for now.
        final long duration = (System.nanoTime() - start) / 1000000;

        // record the actor duration
        this.counterInterface.recordExecutionTime("akka.actor.duration", (int)duration, tags);

        // return null would do the trick, but we want to be _proper_.
        return result;
    }

    /**
     * Advises the {@code ActorCell.handleInvokeFailure(_, failure: Throwable): Unit}
     *
     * @param actorCell the ActorCell where the error spreads from
     * @param failure the exception that escaped from the {@code receive} method
     */
    before(ActorCell actorCell, Throwable failure) : Pointcuts.actorCellHandleInvokeFailure(actorCell, failure) {
        // record the error, general and specific
        String[] tags = getTags(actorCell.self().path(), actorCell.actor());
        this.counterInterface.incrementCounter("akka.actor.error", tags);
        this.counterInterface.incrementCounter(String.format("akka.actor.error.%s", failure.getMessage()), tags);
    }

    /**
     * Advises the {@code EventStream.publish(event: Any): Unit}
     *
     * @param event the received event
     */
    before(Object event) : Pointcuts.eventStreamPublish(event) {
        if (event instanceof UnhandledMessage) {
            UnhandledMessage unhandledMessage = (UnhandledMessage)event;
            String[] tags = getTags(unhandledMessage.recipient().path(), null);
            this.counterInterface.incrementCounter("akka.actor.undelivered", tags);
            this.counterInterface.incrementCounter("akka.actor.undelivered." + unhandledMessage.getMessage().getClass().getSimpleName(), tags);
        }
    }

    /**
     * Advises the {@code actorOf} method of {@code ActorCell} and {@code ActorSystem}
     *
     * @param props the {@code Props} instance used in the call
     * @param actor the {@code ActorRef} returned from the call
     */
    after(Props props) returning (ActorRef actor): Pointcuts.anyActorOf(props) {
        recordActorCreation(props, actor);
    }
    /**
     * Advises the {@code actorOf} method of {@code ActorCell} and {@code ActorSystem} when actor is named in construction
     *
     * @param props the {@code Props} instance used in the call
     * @param actorName the {@code String} used to name the actor at creation site
     * @param actor the {@code ActorRef} returned from the call
     */
    after(Props props, String actorName) returning (ActorRef actor): Pointcuts.namedActorOf(props, actorName) {
        recordActorCreation(props, actor);
    }

    // method containing the recording logic for advising actor creation
    private void recordActorCreation(Props props, ActorRef actor) {
        if (!includeActorPath(new PathAndClass(actor.path(), this.noActorClazz))) return;
        final String uncheckedClassName = uncheckedActorNameFrom(props);
        final Option<String> className = Option.apply(uncheckedClassName);

        this.numberOfActors.putIfAbsent(className, new AtomicInteger(0));
        // increment and get the current number of actors of this type (if the value was 0, then this returns 1 -- which is correct)
        final int currentNumberOfActors = this.numberOfActors.get(className).incrementAndGet();

        // record the current number of actors of this type
        this.counterInterface.recordGaugeValue("akka.actor.count", currentNumberOfActors, uncheckedClassName);
    }

    /**
     * Advises the {@code LocalActorRef.stop} method
     *
     *
     * @param actorCell the {@code ActorCell} of the actor being stopped
     */
    after(ActorCell actorCell) : Pointcuts.actorCellInternalStop(actorCell) {
        if (!includeActorPath(new PathAndClass(actorCell.self().path(), this.noActorClazz))) return;
        final String uncheckedClassName = uncheckedActorNameFrom(actorCell);
        final Option<String> className = Option.apply(uncheckedClassName);

        this.numberOfActors.putIfAbsent(className, new AtomicInteger(0));
        // decrement and get the current number of actors of this type (if the value was 1, then this returns 0 -- which is correct)
        final int currentNumberOfActors = this.numberOfActors.get(className).decrementAndGet();

        this.counterInterface.recordGaugeValue("akka.actor.count", currentNumberOfActors, uncheckedClassName);
    }

}

/*
 * Copyright (c) 2013 original authors
 *
 * 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.eigengo.monitor.agent.akka;

import akka.actor.*;
import org.eigengo.monitor.agent.AgentConfiguration;
import org.eigengo.monitor.output.CounterInterface;
import scala.Option;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Contains advices for monitoring behaviour of an actor; typically imprisoned in an {@code ActorCell}.
 */
public aspect ActorCellMonitoringAspect extends AbstractMonitoringAspect issingleton() {
    private AkkaAgentConfiguration agentConfiguration;
    private final CounterInterface counterInterface;
    private final Option<String> noActorClazz = Option.empty();
    private final ConcurrentHashMap<Option<String>, AtomicLong> samplingCounters;
    private final ConcurrentHashMap<Option<String>, AtomicInteger> numberOfActors;

    /**
     * Constructs this aspect
     */
    public ActorCellMonitoringAspect() {
        AgentConfiguration<AkkaAgentConfiguration> configuration = getAgentConfiguration("akka", AkkaAgentConfigurationJapi.apply());
        this.agentConfiguration = configuration.agent();
        this.counterInterface = createCounterInterface(configuration.common());
        this.samplingCounters = new ConcurrentHashMap<Option<String>, AtomicLong>();
        this.numberOfActors = new ConcurrentHashMap<Option<String>, AtomicInteger>();
    }

    /**
     * Injects the new {@code AkkaAgentConfiguration} instance.
     *
     * @param agentConfiguration the new configuration
     */
    synchronized final void setAgentConfiguration(AkkaAgentConfiguration agentConfiguration) {
        this.agentConfiguration = agentConfiguration;
    }

    // decide whether to include this ActorCell in our measurements
    private boolean includeActorPath(final PathAndClass pathAndClass) {
        if (!this.agentConfiguration.incuded().accept(pathAndClass)) return false;
        if (this.agentConfiguration.excluded().accept(pathAndClass)) return false;

        if (this.agentConfiguration.includeSystemAgents()) return true;

        String userOrSystem = pathAndClass.actorPath().getElements().iterator().next();
        return "user".equals(userOrSystem);
    }

    // get the sample rate for an actor
    private int getSampleRate(final PathAndClass pathAndClass) {
        return this.agentConfiguration.sampling().getRate(pathAndClass);
    }

    // decide whether to sample an actor on a particular occasion
    private final boolean sampleMessage(final PathAndClass pathAndClass) {
        int sampleRate = getSampleRate(pathAndClass);
        if (sampleRate == 1) return true;

        this.samplingCounters.putIfAbsent(pathAndClass.actorClassName(), new AtomicLong(0));
        long timesSeenSoFar = this.samplingCounters.get(pathAndClass.actorClassName()).incrementAndGet();
        return (timesSeenSoFar % sampleRate == 1); // == 1 to log first value (incrementAndGet returns updated value)
    }

    // get the tags for the cell
    private String[] getTags(final ActorPath actorPath, final Actor actor) {
        List<String> tags = new ArrayList<String>();

        // TODO: Improve detection of routed actors. This relies only on the naming :(.
        String lastPathElement = actorPath.elements().last();
        if (lastPathElement.startsWith("$")) {
            // this is routed actor.
            tags.add(actorPath.parent().toString());
            if (this.agentConfiguration.includeRoutees()) tags.add(actorPath.toString());
        } else {
            // there is no supervisor
            tags.add(actorPath.toString());
        }
        if (actor != null) {
            tags.add(String.format("akka:%s.%s", actor.context().system().name(), actor.getClass().getCanonicalName()));
        }

        return tags.toArray(new String[tags.size()]);
    }

    // returns the canonical name of the actor type associated with an ActorCell
    private String uncheckedActorNameFrom(final ActorCell actorCell) {
        return uncheckedActorNameFrom(actorCell.props());
    }

    // returns the canonical name of the actor type associated with a Props instance
    private String uncheckedActorNameFrom(final Props props) {
        return props.actorClass().getCanonicalName();
    }

    /**
     * Advises the {@code ActorCell.receiveMessage(message: Object): Unit}
     * We proceed with the pointcut if the actor is to be included in the monitoring *and* this is
     * the 'multiple-of-n'th time we've seen a message for an actor with a sample rate of n.
     *
     * Currently, we sample queue size, the fact that the message is delivered, the simple name of the class of the
     * message, and the time taken to complete the actor's reactive action.
     *
     * @param actorCell the ActorCell where the actor that receives the message "lives"
     * @param msg the incoming message
     */
    Object around(ActorCell actorCell, Object msg) : Pointcuts.actorCellReceiveMessage(actorCell, msg) {
        final ActorPath actorPath = actorCell.self().path();
        final PathAndClass pathAndClass = new PathAndClass(actorPath, Option.apply(uncheckedActorNameFrom(actorCell)));
        if (!includeActorPath(pathAndClass) || !sampleMessage(pathAndClass)) return proceed(actorCell, msg);

        int samplingRate = getSampleRate(pathAndClass);

        // we tag by actor name
        final String[] tags = getTags(actorPath, actorCell.actor());

        // record the queue size
        this.counterInterface.recordGaugeValue("akka.queue.size", actorCell.numberOfMessages(), tags);
        // record the message, general and specific
        this.counterInterface.incrementCounter("akka.actor.delivered", samplingRate, tags);
        this.counterInterface.incrementCounter("akka.actor.delivered." + msg.getClass().getSimpleName(), samplingRate, tags);

        // measure the time. we're using the ``nanoTime`` call to access the high-precision timer.
        // since we're not really interested in wall time, but just some increasing measure of
        // elapsed time
        Object result = null;
        final long start = System.nanoTime();
        // result will always be ``null``, because target returns ``Unit``
        result = proceed(actorCell, msg);
        // ns or µs is too fine. ms will do for now.
        final long duration = (System.nanoTime() - start) / 1000000;

        // record the actor duration
        this.counterInterface.recordExecutionTime("akka.actor.duration", (int)duration, tags);

        // return null would do the trick, but we want to be _proper_.
        return result;
    }

    /**
     * Advises the {@code ActorCell.handleInvokeFailure(_, failure: Throwable): Unit}
     *
     * @param actorCell the ActorCell where the error spreads from
     * @param failure the exception that escaped from the {@code receive} method
     */
    before(ActorCell actorCell, Throwable failure) : Pointcuts.actorCellHandleInvokeFailure(actorCell, failure) {
        // record the error, general and specific
        String[] tags = getTags(actorCell.self().path(), actorCell.actor());
        this.counterInterface.incrementCounter("akka.actor.error", tags);
        this.counterInterface.incrementCounter(String.format("akka.actor.error.%s", failure.getMessage()), tags);
    }

    /**
     * Advises the {@code EventStream.publish(event: Any): Unit}
     *
     * @param event the received event
     */
    before(Object event) : Pointcuts.eventStreamPublish(event) {
        if (event instanceof UnhandledMessage) {
            UnhandledMessage unhandledMessage = (UnhandledMessage)event;
            String[] tags = getTags(unhandledMessage.recipient().path(), null);
            this.counterInterface.incrementCounter("akka.actor.undelivered", tags);
            this.counterInterface.incrementCounter("akka.actor.undelivered." + unhandledMessage.getMessage().getClass().getSimpleName(), tags);
        }
    }

    /**
     * Advises the {@code actorOf} method of {@code ActorCell} and {@code ActorSystem}
     *
     * @param props the {@code Props} instance used in the call
     * @param actor the {@code ActorRef} returned from the call
     */
    after(Props props) returning (ActorRef actor): Pointcuts.anyActorOf(props) {
        recordActorCreation(props, actor);
    }
    /**
     * Advises the {@code actorOf} method of {@code ActorCell} and {@code ActorSystem} when actor is named in construction
     *
     * @param props the {@code Props} instance used in the call
     * @param actorName the {@code String} used to name the actor at creation site
     * @param actor the {@code ActorRef} returned from the call
     */
    after(Props props, String actorName) returning (ActorRef actor): Pointcuts.namedActorOf(props, actorName) {
        recordActorCreation(props, actor);
    }

    // method containing the recording logic for advising actor creation
    private void recordActorCreation(Props props, ActorRef actor) {
        if (!includeActorPath(new PathAndClass(actor.path(), this.noActorClazz))) return;
        final String uncheckedClassName = uncheckedActorNameFrom(props);
        final Option<String> className = Option.apply(uncheckedClassName);

        this.numberOfActors.putIfAbsent(className, new AtomicInteger(0));
        // increment and get the current number of actors of this type (if the value was 0, then this returns 1 -- which is correct)
        final int currentNumberOfActors = this.numberOfActors.get(className).incrementAndGet();

        // record the current number of actors of this type
        this.counterInterface.recordGaugeValue("akka.actor.count", currentNumberOfActors, uncheckedClassName);
    }

    /**
     * Advises the {@code LocalActorRef.stop} method
     *
     *
     * @param actorCell the {@code ActorCell} of the actor being stopped
     */
    after(ActorCell actorCell) : Pointcuts.actorCellInternalStop(actorCell) {
        if (!includeActorPath(new PathAndClass(actorCell.self().path(), this.noActorClazz))) return;
        final String uncheckedClassName = uncheckedActorNameFrom(actorCell);
        final Option<String> className = Option.apply(uncheckedClassName);

        this.numberOfActors.putIfAbsent(className, new AtomicInteger(0));
        // decrement and get the current number of actors of this type (if the value was 1, then this returns 0 -- which is correct)
        final int currentNumberOfActors = this.numberOfActors.get(className).decrementAndGet();

        this.counterInterface.recordGaugeValue("akka.actor.count", currentNumberOfActors, uncheckedClassName);
    }

}