Bug 248681 - Enum Value matching facility
Summary: Enum Value matching facility
Status: NEW
Alias: None
Product: AspectJ
Classification: Tools
Component: Compiler (show other bugs)
Version: unspecified   Edit
Hardware: PC Windows XP
: P3 enhancement (vote)
Target Milestone: ---   Edit
Assignee: aspectj inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2008-09-26 00:52 EDT by Mohan Radhakrishnan CLA
Modified: 2020-10-23 00:34 EDT (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Mohan Radhakrishnan CLA 2008-09-26 00:52:34 EDT
Matching Enum values is not facilitated with a JoinPoint pattern. Enum code requires Enum value JoinPoints.

Currently the following simple aspect acts when a constant-specific method with a particular Enum value is executed.

	@Pointcut(
	"execution(List<T> getSameValue()) && target(testEnum) && if()")
	public static boolean testPointcut1( TestEnum testEnum ){
		return testEnum == TestEnum.Value2;
	}

	@Before("testPointcut1(testEnum)")
	public void test1( TestEnum testEnum ) {
		System.out.println( "Generics aspect [" + testEnum.ordinal() + "]" );
	}


The Enum is 

public enum TestEnum {
    Value1{
        public <T> List<T> getValue(){ return null; }
        public <T> List<T> getSameValue(){ return null; }
   },
   Value2{
        public <T> Set<T> getValue(){ return null; }
        public <T> List<T> getSameValue(){ return null; }
   };

   abstract <T> Collection<T> getValue();

   abstract <T> Collection<T> getSameValue();

	public static void main(String[] args) {
		System.out.println( Value1.getSameValue() );
		System.out.println( Value2.getSameValue() );
	}

}

Even though this is a simple use case more complicated Enum code is possible.
Comment 1 Andrew Clement CLA 2008-11-27 15:06:47 EST
just bringing it onto the radar, unlikely to make 1.6.3
Comment 2 Andrew Clement CLA 2013-06-24 11:07:00 EDT
unsetting the target field which is currently set for something already released
Comment 3 Alexander Kriegisch CLA 2020-10-21 22:48:06 EDT
Just like Andy said on the mailing list, I am also not really sure what you want here. You did not describe it clearly. My guess is that somehow you wish to limit the target to a specific instance (i.e. to a specific enum value) statically without having to use the dynamic if() pointcut. This would be a completely new feature, not just for enums but for any kind of target. I doubt that we will ever see something like this in AspectJ syntax. The only part of AspectJ where something similar exists is annotation parameter matching where you can limit to parameters matching certain primitive type or enum constants.

What is possible as of today is only what you used plus something like is(EnumType) or !is(EnumType), but that does not help you much in this case.

I am not really sure what you want to achieve with the generics in your enum, so I was tempted to remove them when extending your sample code in order to show what is possible. But I kept them, so you do not think that removing the '<T>' stuff would cause any of the behaviour you see.

------------------------------------------------------------------------

package de.scrum_master.app;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;

public enum TestEnum {
  Value1 {
    public <T> List<T> getValue() {
      return null;
    }

    public <T> List<T> getSameValue() {
      return (List<T>) Arrays.asList(1, 2, 3);
    }
  },
  Value2 {
    public <T> Set<T> getValue() {
      return null;
    }

    public <T> List<T> getSameValue() {
      return (List<T>) Arrays.asList(4, 5, 6);
    }
  };

  abstract <T> Collection<T> getValue();

  abstract <T> Collection<T> getSameValue();

  public static void main(String[] args) {
    System.out.println(Value1.getSameValue());
    System.out.println(Value2.getSameValue());
    System.out.println(new NoEnum().getSameValue());
    new NoEnum().doSomething(Value1);
    new NoEnum().doSomething(Value2);
  }

  public static class NoEnum {
    public <T> List<T> getSameValue() {
      return (List<T>) Arrays.asList(7, 8, 9);
    }

    public void doSomething(TestEnum testEnum) {
      System.out.println(testEnum);
    }
  }

}

------------------------------------------------------------------------

package de.scrum_master.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

import de.scrum_master.app.TestEnum;

@Aspect
public class MyAspect {
  @Pointcut("execution(java.util.List getSameValue()) && target(testEnum) && if()")
  public static boolean testPointcut1(TestEnum testEnum) {
    return testEnum == TestEnum.Value2;
  }

  @Before("testPointcut1(testEnum)")
  public void test1(JoinPoint joinPoint, TestEnum testEnum) {
    System.out.println(joinPoint + " -> " + testEnum);
  }

  @Pointcut("execution(* *(*)) && args(testEnum) && if()")
  public static boolean testPointcut2(TestEnum testEnum) {
    return testEnum == TestEnum.Value2;
  }

  @Before("testPointcut2(testEnum)")
  public void test2(JoinPoint joinPoint, TestEnum testEnum) {
    System.out.println(joinPoint + " -> " + testEnum);
  }

  @Before("execution(* (!is(EnumType)).getSameValue())")
  public void test3(JoinPoint joinPoint) {
    System.out.println(joinPoint + " -> no enum");
  }
}

------------------------------------------------------------------------

Console log:

[1, 2, 3]
execution(List de.scrum_master.app.TestEnum.2.getSameValue()) -> Value2
[4, 5, 6]
execution(List de.scrum_master.app.TestEnum.NoEnum.getSameValue()) -> no enum
[7, 8, 9]
Value1
execution(void de.scrum_master.app.TestEnum.NoEnum.doSomething(TestEnum)) -> Value2
Value2
Comment 4 Mohan Radhakrishnan CLA 2020-10-22 08:33:41 EDT
Thanks. Your code works.

I think my pointcut @Pointcut(
	"execution(List<T> getSameValue()) && target(testEnum) && if()")

wasn't correct. I think it meant that I was trying to statically match using target(testEnum) and also dynamically match using if().

Is this the problem you point out ? I may be misremembering my goal. That was an old issue.
Comment 5 Alexander Kriegisch CLA 2020-10-22 09:00:22 EDT
It was not correct insofar as the '<T>' did not really help and the 'List' class was not fully qualified by its package name. So are you saying that the only problem you had for 12 years is that you couldn't get the syntax straight and there is not really any new AspectJ feature you need? I am asking directly in order to understand better.
Comment 6 Alexander Kriegisch CLA 2020-10-22 09:02:29 EDT
Besides, if you don't remember the purpose of this issue yourself, why did you bring it up again on the mailing list? I thought you had a specific problem.

Anyway, I am glad that the problem seems to be solved. Would you agree that the issue be closed by a maintainer (Andy usually)?
Comment 7 Mohan Radhakrishnan CLA 2020-10-22 09:51:50 EDT
Yes. You can close it. I have a specific problem now( didn't use AspectJ during these years ) and I remembered the old issue. I will reply to Andy's mail.
Comment 8 Mohan Radhakrishnan CLA 2020-10-22 10:00:34 EDT
A question I have is this. The generics syntax isn't useful for this code when I use AspectJ. Is that what you mean ? Are there examples that use the generics syntax ?
Comment 9 Mohan Radhakrishnan CLA 2020-10-22 10:51:46 EDT
package state;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class EnumAspect {
	  @Pointcut("execution(java.util.List getValue()) && target(testEnum) && if()")
	  public static boolean testPointcut1(TestEnum testEnum) {
	    return testEnum == TestEnum.Value2;
	  }

	  @Before("testPointcut1(testEnum)")
	  public void test1(JoinPoint joinPoint, TestEnum testEnum) {
	    System.out.println(joinPoint + " -> " + testEnum);
	  }

	  @Pointcut("execution(* *(*)) && args(testEnum) && if()")
	  public static boolean testPointcut2(TestEnum testEnum) {
	    return testEnum == TestEnum.Value2;
	  }

	  @Before("testPointcut2(testEnum)")
	  public void test2(JoinPoint joinPoint, TestEnum testEnum) {
	    System.out.println(joinPoint + " -> " + testEnum);
	  }

	  @Before("execution(* (!is(EnumType)).getValue())")
	  public void test3(JoinPoint joinPoint) {
	    System.out.println(joinPoint + " -> no enum");
	  }
}


package state;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;

public enum TestEnum {
	  Value1 {
	    public <T> List<T> getValue() {
	      return null;
	    }

//	    public <T> List<T> getSameValue() {
//	      return (List<T>) Arrays.asList(1, 2, 3);
//	    }
	  },
	  Value2 {
	    public <T> Set<T> getValue() {
	      return null;
	    }

//	    public <T> List<T> getSameValue() {
//	      return (List<T>) Arrays.asList(4, 5, 6);
//	    }
	  };

	  abstract <T> Collection<T> getValue();

	  //abstract <T> Collection<T> getSameValue();

	  public static void main(String[] args) {
	    System.out.println(Value1.getValue());
	    System.out.println(Value2.getValue());
	  }

	}

Is this code missing something that your doesn't ? Asking because it doesn't work for me.
Comment 10 Alexander Kriegisch CLA 2020-10-22 20:35:53 EDT
You changed the sample code, so the pointcut does not match anymore:

Value1.getValue() does not match as planned, because the 'if()' pointcut limits to Value2.

Value2.getValue() does not match, because you changed the method to return a Set, not a List.

So if you the pointcut expression for adjust testPointcut1 to

execution(java.util.Collection getValue()) && target(testEnum) && if()

or, more explicitly, to

execution(java.util.Collection+ getValue()) && target(testEnum) && if()

your test1 advice should get triggered.
Comment 11 Alexander Kriegisch CLA 2020-10-22 20:39:36 EDT
As for your other question, here is a chapter about generics in AspectJ which you might want to read. Maybe I should read it again, too. I do not use generics a lot in connection with AspectJ.

https://www.eclipse.org/aspectj/doc/released/adk15notebook/generics-inAspectJ5.html
Comment 12 Alexander Kriegisch CLA 2020-10-22 20:39:49 EDT
As for your other question, here is a chapter about generics in AspectJ which you might want to read. Maybe I should read it again, too. I do not use generics a lot in connection with AspectJ.

https://www.eclipse.org/aspectj/doc/released/adk15notebook/generics-inAspectJ5.html
Comment 13 Alexander Kriegisch CLA 2020-10-23 00:34:28 EDT
Like I said before, using generics does not really make sense in an enum, it would not have any effect as far as I can see. How about this?

------------------------------------------------------------------------

package state;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public enum TestEnum {
    Value1 {
      public List<Integer> getValue() {
        return Arrays.asList(1, 2, 3);
      }

      public List<Integer> getSameValue() {
        return Arrays.asList(4, 5, 6);
      }
    },
    Value2 {
      public Set<String> getValue() {
        return new HashSet<String>(Arrays.asList("foo", "bar", "zot"));
      }

      public Set<String> getSameValue() {
        return new HashSet<String>(Arrays.asList("one", "two", "three"));
      }
    };

  abstract Collection<?> getValue();
  abstract Collection<?> getSameValue();

  public static void main(String[] args) {
    System.out.println(Value1.getValue());
    System.out.println(Value1.getSameValue());
    System.out.println(Value2.getValue());
    System.out.println(Value2.getSameValue());
  }

}

------------------------------------------------------------------------

Please note that you can still make both constants return different Collection<?> types such as List<Integer> or Set<String>, why ever you would want to do that. (Maybe in order to make the program harder to read and maintain.)

------------------------------------------------------------------------

package state;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class EnumAspect {
  @Pointcut("execution(java.util.Collection getValue()) && target(testEnum) && if()")
  public static boolean testPointcut1(TestEnum testEnum) {
    return testEnum == TestEnum.Value2;
  }

  @Before("testPointcut1(testEnum)")
  public void test1(JoinPoint joinPoint, TestEnum testEnum) {
    System.out.println(joinPoint + " -> " + testEnum);
  }
}

------------------------------------------------------------------------

Now the console log would be:

------------------------------------------------------------------------

[1, 2, 3]
[4, 5, 6]
execution(Set state.TestEnum.2.getValue()) -> Value2
[bar, foo, zot]
[one, two, three]