/**************************************************************************************
* Copyright (c) Jonas Bon�r, Alexandre Vasseur. All rights reserved. *
* http://aspectwerkz.codehaus.org *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the LGPL license *
* a copy of which has been included with this distribution in the license.txt file. *
**************************************************************************************/
package org.codehaus.aspectwerkz.aspect.management;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Collections;
import org.codehaus.aspectwerkz.IndexTuple;
import org.codehaus.aspectwerkz.NameIndexTuple;
import org.codehaus.aspectwerkz.SystemLoader;
import org.codehaus.aspectwerkz.definition.expression.Expression;
import org.codehaus.aspectwerkz.definition.PointcutDefinition;
/**
* Implementation of the pointcut concept. I.e. an abstraction of a well defined point of execution in the program.<br/>
* Could matches one or many as long at it is well defined.<br/>
* Stores the advices for the specific pointcut.
*
* @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r</a>
*/
public class Pointcut implements Serializable {
/**
* The expression for the pointcut.
*/
protected Expression m_expression;
/**
* The cflow pointcut expression.
*/
protected String m_cflowExpression;
/**
* The names of the around advices.
*/
protected String[] m_aroundAdviceNames = new String[0];
/**
* The names of the around advices.
*/
protected String[] m_beforeAdviceNames = new String[0];
/**
* The names of the around advices.
*/
protected String[] m_afterAdviceNames = new String[0];
/**
* The indexes of the around advices.
*/
protected IndexTuple[] m_aroundAdviceIndexes = new IndexTuple[0];
/**
* The indexes of the before advices.
*/
protected IndexTuple[] m_beforeAdviceIndexes = new IndexTuple[0];
/**
* The indexes of the after advices.
*/
protected IndexTuple[] m_afterAdviceIndexes = new IndexTuple[0];
/**
* The UUID for the AspectWerkz system.
*/
protected String m_uuid;
/**
* Creates a new pointcut.
*
* @param uuid the UUID for the AspectWerkz system
* @param expression the pattern for the pointcut
*/
public Pointcut(final String uuid, final Expression expression) {
if (uuid == null) {
throw new IllegalArgumentException("uuid can not be null");
}
if (expression == null) {
throw new IllegalArgumentException("expression be null");
}
m_uuid = uuid;
m_expression = expression;
}
/**
* Adds an advice to the pointcut.
*
* @param advice the name of the advice to add
*/
public void addAroundAdvice(final String advice) {
if (advice == null || advice.trim().length() == 0) {
throw new IllegalArgumentException("name of advice to add can not be null or an empty string");
}
// system reinitialization can lead to redundancy
// TODO: make the HotSwap with def slower
for (int i = 0; i < m_aroundAdviceNames.length; i++) {
if (advice.equals(m_aroundAdviceNames[i])) {
return;
}
}
synchronized (m_aroundAdviceNames) {
synchronized (m_aroundAdviceIndexes) {
final String[] tmp = new String[m_aroundAdviceNames.length + 1];
System.arraycopy(m_aroundAdviceNames, 0, tmp, 0, m_aroundAdviceNames.length);
tmp[m_aroundAdviceNames.length] = advice;
m_aroundAdviceNames = new String[m_aroundAdviceNames.length + 1];
System.arraycopy(tmp, 0, m_aroundAdviceNames, 0, tmp.length);
// update the indexes
m_aroundAdviceIndexes = new IndexTuple[m_aroundAdviceNames.length];
for (int i = 0, j = m_aroundAdviceNames.length; i < j; i++) {
m_aroundAdviceIndexes[i] = SystemLoader.getSystem(m_uuid).
getAspectManager().getAdviceIndexFor(m_aroundAdviceNames[i]);
}
}
}
}
/**
* Adds an advice to the pointcut.
*
* @param advice the name of the advice to add
*/
public void addBeforeAdvice(final String advice) {
if (advice == null || advice.trim().length() == 0) {
throw new IllegalArgumentException("name of advice to add can not be null or an empty string");
}
synchronized (m_beforeAdviceNames) {
synchronized (m_beforeAdviceIndexes) {
final String[] tmp = new String[m_beforeAdviceNames.length + 1];
System.arraycopy(m_beforeAdviceNames, 0, tmp, 0, m_beforeAdviceNames.length);
tmp[m_beforeAdviceNames.length] = advice;
m_beforeAdviceNames = new String[m_beforeAdviceNames.length + 1];
System.arraycopy(tmp, 0, m_beforeAdviceNames, 0, tmp.length);
// update the indexes
m_beforeAdviceIndexes = new IndexTuple[m_beforeAdviceNames.length];
for (int i = 0, j = m_beforeAdviceNames.length; i < j; i++) {
m_beforeAdviceIndexes[i] = SystemLoader.getSystem(m_uuid).
getAspectManager().getAdviceIndexFor(m_beforeAdviceNames[i]);
}
}
}
}
/**
* Adds an advice to the pointcut.
*
* @param advice the name of the advice to add
*/
public void addAfterAdvice(final String advice) {
if (advice == null || advice.trim().length() == 0) {
throw new IllegalArgumentException("name of advice to add can not be null or an empty string");
}
synchronized (m_afterAdviceNames) {
synchronized (m_afterAdviceIndexes) {
final String[] tmp = new String[m_afterAdviceNames.length + 1];
System.arraycopy(m_afterAdviceNames, 0, tmp, 0, m_afterAdviceNames.length);
tmp[m_afterAdviceNames.length] = advice;
m_afterAdviceNames = new String[m_afterAdviceNames.length + 1];
System.arraycopy(tmp, 0, m_afterAdviceNames, 0, tmp.length);
// update the indexes
m_afterAdviceIndexes = new IndexTuple[m_afterAdviceNames.length];
for (int i = 0, j = m_afterAdviceNames.length; i < j; i++) {
m_afterAdviceIndexes[i] = SystemLoader.getSystem(m_uuid).
getAspectManager().getAdviceIndexFor(m_afterAdviceNames[i]);
}
}
}
}
/**
* Removes an advice from the pointcut.
*
* @param advice the name of the advice to remove
*/
public void removeAroundAdvice(final String advice) {
if (advice == null || advice.trim().length() == 0) {
throw new IllegalArgumentException("name of advice to remove can not be null or an empty string");
}
synchronized (m_aroundAdviceNames) {
synchronized (m_aroundAdviceIndexes) {
int index = -1;
for (int i = 0; i < m_aroundAdviceNames.length; i++) {
if (m_aroundAdviceNames[i].equals(advice)) {
index = i;
break;
}
}
if (index == -1) {
throw new RuntimeException("can not remove advice with the name " + advice + ": no such advice");
}
final String[] names = new String[m_aroundAdviceNames.length - 1];
int j, k;
for (j = 0, k = 0; j < index; j++, k++) {
names[j] = m_aroundAdviceNames[j];
}
j++;
for (; j < m_aroundAdviceNames.length; j++, k++) {
names[k] = m_aroundAdviceNames[j];
}
m_aroundAdviceNames = new String[names.length];
System.arraycopy(names, 0, m_aroundAdviceNames, 0, names.length);
final IndexTuple[] indexes = new IndexTuple[m_aroundAdviceIndexes.length - 1];
for (j = 0, k = 0; j < index; j++, k++) {
indexes[j] = m_aroundAdviceIndexes[j];
}
j++;
for (; j < m_aroundAdviceIndexes.length; j++, k++) {
indexes[k] = m_aroundAdviceIndexes[j];
}
m_aroundAdviceIndexes = new IndexTuple[indexes.length];
System.arraycopy(indexes, 0, m_aroundAdviceIndexes, 0, indexes.length);
}
}
}
/**
* Removes an advice from the pointcut.
*
* @param advice the name of the advice to remove
*/
public void removeBeforeAdvice(final String advice) {
if (advice == null || advice.trim().length() == 0) {
throw new IllegalArgumentException("name of advice to remove can not be null or an empty string");
}
synchronized (m_beforeAdviceNames) {
synchronized (m_beforeAdviceIndexes) {
int index = -1;
for (int i = 0; i < m_beforeAdviceNames.length; i++) {
if (m_beforeAdviceNames[i].equals(advice)) {
index = i;
break;
}
}
if (index == -1) {
throw new RuntimeException("can not remove advice with the name " + advice + ": no such advice");
}
final String[] names = new String[m_beforeAdviceNames.length - 1];
int j, k;
for (j = 0, k = 0; j < index; j++, k++) {
names[j] = m_beforeAdviceNames[j];
}
j++;
for (; j < m_beforeAdviceNames.length; j++, k++) {
names[k] = m_beforeAdviceNames[j];
}
m_beforeAdviceNames = new String[names.length];
System.arraycopy(names, 0, m_beforeAdviceNames, 0, names.length);
final IndexTuple[] indexes = new IndexTuple[m_beforeAdviceIndexes.length - 1];
for (j = 0, k = 0; j < index; j++, k++) {
indexes[j] = m_beforeAdviceIndexes[j];
}
j++;
for (; j < m_beforeAdviceIndexes.length; j++, k++) {
indexes[k] = m_beforeAdviceIndexes[j];
}
m_beforeAdviceIndexes = new IndexTuple[indexes.length];
System.arraycopy(indexes, 0, m_beforeAdviceIndexes, 0, indexes.length);
}
}
}
/**
* Removes an advice from the pointcut.
*
* @param advice the name of the advice to remove
*/
public void removeAfterAdvice(final String advice) {
if (advice == null || advice.trim().length() == 0) {
throw new IllegalArgumentException("name of advice to remove can not be null or an empty string");
}
synchronized (m_afterAdviceNames) {
synchronized (m_afterAdviceIndexes) {
int index = -1;
for (int i = 0; i < m_afterAdviceNames.length; i++) {
if (m_afterAdviceNames[i].equals(advice)) {
index = i;
break;
}
}
if (index == -1) {
throw new RuntimeException("can not remove advice with the name " + advice + ": no such advice");
}
final String[] names = new String[m_afterAdviceNames.length - 1];
int j, k;
for (j = 0, k = 0; j < index; j++, k++) {
names[j] = m_afterAdviceNames[j];
}
j++;
for (; j < m_afterAdviceNames.length; j++, k++) {
names[k] = m_afterAdviceNames[j];
}
m_afterAdviceNames = new String[names.length];
System.arraycopy(names, 0, m_afterAdviceNames, 0, names.length);
final IndexTuple[] indexes = new IndexTuple[m_afterAdviceIndexes.length - 1];
for (j = 0, k = 0; j < index; j++, k++) {
indexes[j] = m_afterAdviceIndexes[j];
}
j++;
for (; j < m_afterAdviceIndexes.length; j++, k++) {
indexes[k] = m_afterAdviceIndexes[j];
}
m_afterAdviceIndexes = new IndexTuple[indexes.length];
System.arraycopy(indexes, 0, m_afterAdviceIndexes, 0, indexes.length);
}
}
}
/**
* Checks if the pointcuts has a certain advice.
*
* @param advice the advice to check for existence
* @return boolean
*/
public boolean hasAroundAdvice(final String advice) {
for (int i = 0; i < m_aroundAdviceNames.length; i++) {
if (m_aroundAdviceNames[i].equals(advice)) {
return true;
}
}
return false;
}
/**
* Checks if the pointcuts has a certain advice.
*
* @param advice the advice to check for existence
* @return boolean
*/
public boolean hasBeforeAdvice(final String advice) {
for (int i = 0; i < m_beforeAdviceNames.length; i++) {
if (m_beforeAdviceNames[i].equals(advice)) {
return true;
}
}
return false;
}
/**
* Checks if the pointcuts has a certain advice.
*
* @param advice the advice to check for existence
* @return boolean
*/
public boolean hasAfterAdvice(final String advice) {
for (int i = 0; i < m_afterAdviceNames.length; i++) {
if (m_afterAdviceNames[i].equals(advice)) {
return true;
}
}
return false;
}
/**
* Returns the cflow expression.
*
* @return the cflow expression
*/
public String getCFlowExpression() {
return m_cflowExpression;
}
/**
* Sets the cflow expression.
*
* @param cflowExpression the cflow expression
*/
public void setCFlowExpression(final String cflowExpression) {
m_cflowExpression = cflowExpression;
}
/**
* Returns the advices in the form of an array with advice/index tuples. To be used when a reordering of the advices
* is necessary.<br/> For addition of an advice see <code>addAdviceTestMethod(..)</code>.<br/> For removal of an
* advice see <code>removeAdviceTestMethod(..)</code>.
*
* @return the current advice/index tuples as a list
*/
public List getAroundAdviceIndexTuples() {
synchronized (m_aroundAdviceIndexes) {
synchronized (m_aroundAdviceNames) {
final List advices = new ArrayList(m_aroundAdviceNames.length);
for (int i = 0; i < m_aroundAdviceNames.length; i++) {
advices.add(new NameIndexTuple(m_aroundAdviceNames[i], m_aroundAdviceIndexes[i]));
}
return advices;
}
}
}
/**
* Returns the advices in the form of an array with advice/index tuples. To be used when a reordering of the advices
* is necessary.<br/> For addition of an advice see <code>addAdviceTestMethod(..)</code>.<br/> For removal of an
* advice see <code>removeAdviceTestMethod(..)</code>.
*
* @return the current advice/index tuples as a list
*/
public List getBeforeAdviceIndexTuples() {
synchronized (m_beforeAdviceIndexes) {
synchronized (m_beforeAdviceNames) {
final List advices = new ArrayList(m_beforeAdviceNames.length);
for (int i = 0; i < m_beforeAdviceNames.length; i++) {
advices.add(new NameIndexTuple(m_beforeAdviceNames[i], m_beforeAdviceIndexes[i]));
}
return advices;
}
}
}
/**
* Returns the advices in the form of an array with advice/index tuples. To be used when a reordering of the advices
* is necessary.<br/> For addition of an advice see <code>addAdviceTestMethod(..)</code>.<br/> For removal of an
* advice see <code>removeAdviceTestMethod(..)</code>.
*
* @return the current advice/index tuples as a list
*/
public List getAfterAdviceIndexTuples() {
synchronized (m_afterAdviceIndexes) {
synchronized (m_afterAdviceNames) {
final List advices = new ArrayList(m_afterAdviceNames.length);
for (int i = 0; i < m_afterAdviceNames.length; i++) {
advices.add(new NameIndexTuple(m_afterAdviceNames[i], m_afterAdviceIndexes[i]));
}
return advices;
}
}
}
/**
* Sets the advices. To be used when a reordering of the advices is necessary.<br/> For addition of an advice see
* <code>addAdviceTestMethod(..)</code>.<br/> For removal of an advice see <code>removeAdviceTestMethod(..)</code>.
*
* @param advices the new advice/index tuple array
*/
public void setAroundAdviceIndexTuples(final List advices) {
synchronized (m_aroundAdviceIndexes) {
synchronized (m_aroundAdviceNames) {
m_aroundAdviceNames = new String[advices.size()];
m_aroundAdviceIndexes = new IndexTuple[advices.size()];
int i = 0;
for (Iterator it = advices.iterator(); it.hasNext(); i++) {
try {
NameIndexTuple tuple = (NameIndexTuple)it.next();
m_aroundAdviceNames[i] = tuple.getName();
m_aroundAdviceIndexes[i] = tuple.getIndex();
}
catch (ClassCastException e) {
throw new RuntimeException("advice list must only contain AdviceIndexTuples");
}
}
}
}
}
/**
* Sets the advices. To be used when a reordering of the advices is necessary.<br/> For addition of an advice see
* <code>addAdviceTestMethod(..)</code>.<br/> For removal of an advice see <code>removeAdviceTestMethod(..)</code>.
*
* @param advices the new advice/index tuple array
*/
public void setBeforeAdviceIndexTuples(final List advices) {
synchronized (m_beforeAdviceIndexes) {
synchronized (m_beforeAdviceNames) {
m_beforeAdviceNames = new String[advices.size()];
m_beforeAdviceIndexes = new IndexTuple[advices.size()];
int i = 0;
for (Iterator it = advices.iterator(); it.hasNext(); i++) {
try {
NameIndexTuple tuple = (NameIndexTuple)it.next();
m_beforeAdviceNames[i] = tuple.getName();
m_beforeAdviceIndexes[i] = tuple.getIndex();
}
catch (ClassCastException e) {
throw new RuntimeException("advice list must only contain AdviceIndexTuples");
}
}
}
}
}
/**
* Sets the advices. To be used when a reordering of the advices is necessary.<br/> For addition of an advice see
* <code>addAdviceTestMethod(..)</code>.<br/> For removal of an advice see <code>removeAdviceTestMethod(..)</code>.
*
* @param advices the new advice/index tuple array
*/
public void setAfterAdviceIndexTuples(final List advices) {
synchronized (m_afterAdviceIndexes) {
synchronized (m_afterAdviceNames) {
m_afterAdviceNames = new String[advices.size()];
m_afterAdviceIndexes = new IndexTuple[advices.size()];
int i = 0;
for (Iterator it = advices.iterator(); it.hasNext(); i++) {
try {
NameIndexTuple tuple = (NameIndexTuple)it.next();
m_afterAdviceNames[i] = tuple.getName();
m_afterAdviceIndexes[i] = tuple.getIndex();
}
catch (ClassCastException e) {
throw new RuntimeException("advice list must only contain AdviceIndexTuples");
}
}
}
}
}
/**
* Returns a specific advice index.
*
* @return the advice index
*/
public IndexTuple getAroundAdviceIndex(final int index) {
return m_aroundAdviceIndexes[index];
}
/**
* Returns a specific advice index.
*
* @return the advice index
*/
public IndexTuple getBeforeAdviceIndex(final int index) {
return m_beforeAdviceIndexes[index];
}
/**
* Returns a specific advice index.
*
* @return the advice index
*/
public IndexTuple getAfterAdviceIndex(final int index) {
return m_afterAdviceIndexes[index];
}
/**
* Returns a list with the indexes for the around advices for the pointcut.
*
* @return the advices
*/
public IndexTuple[] getAroundAdviceIndexes() {
return m_aroundAdviceIndexes;
}
/**
* Returns a list with the indexes for the before advices for the pointcut.
*
* @return the advices
*/
public IndexTuple[] getBeforeAdviceIndexes() {
return m_beforeAdviceIndexes;
}
/**
* Returns a list with the indexes for the after advices for the pointcut.
*
* @return the advices
*/
public IndexTuple[] getAfterAdviceIndexes() {
return m_afterAdviceIndexes;
}
/**
* Returns a list with the names for the advices for the pointcut.
*
* @return the advices
*/
public String[] getAroundAdviceNames() {
return m_aroundAdviceNames;
}
/**
* Returns the expression for the pointcut.
*
* @return the expression
*/
public Expression getExpression() {
return m_expression;
}
/**
* Provides custom deserialization.
*
* @param stream the object input stream containing the serialized object
* @throws java.lang.Exception in case of failure
*/
private void readObject(final ObjectInputStream stream) throws Exception {
ObjectInputStream.GetField fields = stream.readFields();
m_expression = (Expression)fields.get("m_expression", null);
m_aroundAdviceNames = (String[])fields.get("m_aroundAdviceNames", null);
m_aroundAdviceIndexes = (IndexTuple[])fields.get("m_aroundAdviceIndexes", null);
m_beforeAdviceNames = (String[])fields.get("m_beforeAdviceNames", null);
m_beforeAdviceIndexes = (IndexTuple[])fields.get("m_beforeAdviceIndexes", null);
m_afterAdviceNames = (String[])fields.get("m_afterAdviceNames", null);
m_afterAdviceIndexes = (IndexTuple[])fields.get("m_afterAdviceIndexes", null);
m_uuid = (String)fields.get("m_uuid", null);
}
}