Getter and Setter AST Transformation - groovy

I wrote my own AST Transformation which should generate getter and setter methods (here creating getter method). But they don't work and can't understand reason.
create annotation with property
#Retention(RetentionPolicy.SOURCE)
#Target([ElementType.FIELD])
#GroovyASTTransformationClass(['ua.home.gag.ast.GetterAndSetterASTTransformation'])
public #interface GetterAndSetter {
}
my code of AST transformation which should create getter method for annotated field
#GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
class GetterAndSetterASTTransformation implements ASTTransformation {
#Override
void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {
if (!checkNodes(astNodes)) return
List fields = astNodes.findAll { it instanceof FieldNode }
fields.each {
MethodNode getter = getterMethod(ClassHelper.make(it));
it.declaringClass.addMethod(getter);
}
}
static def checkNodes(ASTNode[] nodes) {
nodes &&
nodes[0] &&
nodes[1] &&
nodes[0] instanceof AnnotationNode &&
nodes[0].classNode?.name == GetterAndSetter.class.name &&
nodes[1] instanceof ClassNode
}
private MethodNode getterMethod(FieldNode fieldNode) {
return new MethodNode(
"getMy" + fieldNode.name.capitalize(),
Modifier.PUBLIC,
new ClassNode(fieldNode.type),
new Parameter[0],
new ClassNode[0],
new BlockStatement(
[new ReturnStatement(
new VariableExpression(fieldNode.name)
)],
new VariableScope())
)
}
}
Annotation check
import ua.home.gag.ast.GetterAndSetter
class Example {
#GetterAndSetter
int counter = 5;
static void main(String[] args) {
println new Example().getMyCounter();
}
}
In which place I did mistake?
The result of running :
Exception in thread "main" groovy.lang.MissingMethodException: No signature of method: ua.home.gag.usage.Example.getMyCounter() is applicable for argument types: () values: []
Possible solutions: getCounter(), setCounter(int)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:56)
at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:51)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:112)
at ua.home.gag.usage.Example.main(Example.groovy:12)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
PS repo https://bitbucket.org/maxrdev/ast-gands

I think that the problem is in your checkNodes method. This expression nodes[1] instanceof ClassNode evaluates to false because nodes[1] is instanceof FieldNode.
You also don't have to filter and iterate over the fields, because this transformation will be applied to all fields annotated with #GetterAndSetter. That's why you have to only focus on a single case for annotated field. Usually all you have to do is:
#GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
class GetterAndSetterASTTransformation implements ASTTransformation {
#Override
void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {
AnnotationNode parent = (AnnotationNode) astNodes[0]
FieldNode node = (FieldNode) astNodes[1]
if (!(parent instanceof AnnotationNode) && !(node instanceof FieldNode)) {
throw new RuntimeException("Internal error: wrong types: ${node.class} / ${parent.class}");
}
Statement statement = new BlockStatement([
new ReturnStatement(new VariableExpression(node))
], new VariableScope())
MethodNode methodNode = new MethodNode("getMy${node.name.capitalize()}",
Modifier.PUBLIC,
node.type,
new Parameter[0],
new ClassNode[0],
statement
)
node.declaringClass.addMethod(methodNode)
}
}
Then below code will work:
class Example {
#GetterAndSetter
int counter = 5;
#GetterAndSetter
String lorem = 'asdasd'
#Deprecated
#GetterAndSetter
BigDecimal ipsum = BigDecimal.ONE
static void main(String[] args) {
Example example = new Example()
println example.getMyCounter()
println example.getMyLorem()
println example.getMyIpsum()
}
}
And the result is:
/usr/lib/jvm/java-1.8.0/bin/java -Didea.launcher.port=7541 -Didea.launcher.bin.path=/opt/idea-IU-129.1525/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-1.8.0/jre/lib/resources.jar:/usr/lib/jvm/java-1.8.0/jre/lib/jsse.jar:/usr/lib/jvm/java-1.8.0/jre/lib/jce.jar:/usr/lib/jvm/java-1.8.0/jre/lib/charsets.jar:/usr/lib/jvm/java-1.8.0/jre/lib/management-agent.jar:/usr/lib/jvm/java-1.8.0/jre/lib/rt.jar:/usr/lib/jvm/java-1.8.0/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-1.8.0/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-1.8.0/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-1.8.0/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-1.8.0/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-1.8.0/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-1.8.0/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-1.8.0/jre/lib/ext/sunjce_provider.jar:/home/wololock/workspace/idea/ast-gands/target/classes:/home/wololock/.m2/repository/org/codehaus/groovy/groovy-all/2.3.7/groovy-all-2.3.7.jar:/opt/idea-IU-129.1525/lib/idea_rt.jar com.intellij.rt.execution.application.AppMain ua.home.gag.usage.Example
5
asdasd
1
Process finished with exit code 0
You can find more examples in e.g. grails-core repository - go to https://github.com/grails/grails-core/, type t and search for ASTTransformation

Related

How do I define a type-aware groovy DSL which can propagate a type to inner blocks?

I'm trying to implement a Groovy DSL, which takes a class name in a top-level block parameter, then allows static type checking against methods of that class in inner blocks, without needing to re-declare that class name redundantly.
(I am using Groovy v2.5.6)
For example, given this class:
// A message data type to parse
#Canonical
class MessageX {
int size
boolean hasName
// an optional field to read, depending on whether #hasName is true
String name
}
I'd want to be able to define things in the DSL something like this:
parserFor(MessageX) {
readInt 'size'
readBool 'hasName'
// #hasName is a property of MessageX
when { hasName } {
readString 'name'
}
}
An attempt at implementing this might be:
import groovy.transform.Canonical
import groovy.transform.CompileStatic
#CompileStatic
#Canonical
class MessageX {
boolean hasName
String name
int size
}
/** Generic message builder class for parsing messages */
#CompileStatic
class MessageBlock<T> {
MessageBlock(Map<String, Object> values) {
this.value = values
}
private final Map<String, Object> value
void readString(String field) {
// ...
}
void readInt(String field) {
// ..
}
void readBool(String field) {
// ...
}
/** Defines a condition */
void when(#DelegatesTo(type = "T") Closure conditionBlock, #DelegatesTo(value = MessageBlock, type = "MessageBlock<T>") Closure bodyBlock) {
// ...
}
}
#CompileStatic
class Parser {
static final <T> Closure<T> parserFor(Class<T> type, #DelegatesTo(value = MessageBlock, type = "MessageBlock<T>") Closure block) {
println "parser get type $type.name"
return {
Map<String, Object> values = [:]
MessageBlock<T> blockImpl = new MessageBlock<T>(values);
block.delegate = blockImpl
block()
return type.newInstance(values)
}
}
static void build() {
// Define a parser using our DSL
Closure<MessageX> messageXParser = parserFor(MessageX) {
readBool 'hasName'
when { hasName } {
readString 'name'
}
}
messageXParser()
}
}
Parser.build()
The documentation here suggests this should be possible with just the type = "MessageBlock<T>" tag on DelegatesTo.
However, that gives me null pointer exceptions when compiling.
Caught: BUG! exception in phase 'instruction selection' in source unit 'test.groovy' unexpected NullpointerException
BUG! exception in phase 'instruction selection' in source unit 'test.groovy' unexpected NullpointerException
Caused by: java.lang.NullPointerException
In the above example I also have the value = MessageBlock tag, which at least avoids an NPE - but I still get an error:
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
test.groovy: 57: [Static type checking] - The variable [hasName] is undeclared.
# line 57, column 20.
when { hasName } {
^
I have not yet found a way to get the #when method's closure block to correctly delegate to MessageX class. I've tried these annotations for the second parameter to #parserFor and various other permutations, to no avail:
DelegatesTo(MessageX)
DelegatesTo(value = MessageX, type = "T")
DelegatesTo(value = MessageX, type = "MessageX<T>")
DelegatesTo(type = "MessageX<T>")
Can anyone help?

Adding a field with Groovy AST doesn't generate getter and setter

So I'm writing an AST Transformation that adds a field with the #Delegate annotation to a class
#SimpleAST
class PersonBuilder{
}
Should produce
class PersonBuilder{
#Delegate
Person target = new Person
}
My Interface:
#Retention(RetentionPolicy.RUNTIME)
#Target([ElementType.TYPE])
#GroovyASTTransformationClass("poc.SimpleASTTransformation")
public #interface SimpleAST {
/**
* A class for which builder methods should be created. It will be an error to leave
* this attribute with its default value for some strategies.
*/
Class value()
}
My transformation:
#CompileStatic
#GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
class SimpleASTTransformation implements ASTTransformation {
#Override
void visit(ASTNode[] astNodes, SourceUnit source) {
ClassNode classNode = (ClassNode) astNodes[1]
if (!astNodes) return
if (!astNodes[0]) return
if (!astNodes[1]) return
if (!(astNodes[0] instanceof AnnotationNode)) return
if (!(astNodes[1] instanceof ClassNode)) return
println "Running AST Transformation for ${classNode.getNameWithoutPackage()}..."
AnnotationNode annotationNode = (AnnotationNode) astNodes[0]
ClassExpression classExpression = (ClassExpression) annotationNode.getMember("value")
String packageName = classNode.getPackageName()
String builderClassNameWithoutPackage = classNode.getNameWithoutPackage()
String originalClassNameWithPackage = classExpression.getText()
originalClassNameWithPackage = Validate.checkOriginalClassName(originalClassNameWithPackage)
Class<?> originalClass = Class.forName(originalClassNameWithPackage)
ClassNode originalClassNode = new ClassNode(originalClass)
String originalClassNameWithoutPackage = originalClassNode.getNameWithoutPackage()
println "Generating methods for $originalClassNameWithoutPackage..."
generateTargetField(classNode,originalClass)
println "Transformation applied!"
}
static void generateTargetField(ClassNode classNode, Class originalClass){
ClassNode originalClassNode = new ClassNode(originalClass)
ConstructorCallExpression constructorCallExpression = new ConstructorCallExpression(originalClassNode,new ArgumentListExpression())
FieldNode fieldNode = new FieldNode("target",
2,
originalClassNode,
classNode,
constructorCallExpression)
ArrayList<AnnotationNode> annotationNodes = new ArrayList<>()
annotationNodes.add(new AnnotationNode(new ClassNode(Delegate)))
fieldNode.addAnnotations(annotationNodes)
classNode.addField(fieldNode)
}
}
When I check the byte code there is no getter/setter methods for the fields inside the Class Person (I used #Delegate on the target field)
However, if I Just add the field manually and compile the code I get getter and setters for the fields in class Person.
Person has 2 fields: firstName, lastName both Strings.

What is the static version of propertyMissing method in Groovy?

ok - tried looking /reading and not sure i have an answer to this.
I have a Utility class which wraps a static ConcurrentLinkedQueue internally.
The utility class itself adds some static methods - i dont expect to call new to create an instance of the Utility.
I want to intercept the getProperty calls the utility class - and implement these internally in the class definition
I can achieve this by adding the following to the utility classes metaclass, before i use it
UnitOfMeasure.metaClass.static.propertyMissing = {name -> println "accessed prop called $name"}
println UnitOfMeasure.'Each'
however what i want to do is declare the interception in the class definition itself. i tried this in the class definition - but it never seems to get called
static def propertyMissing (receiver, String propName) {
println "prop $propName, saught"
}
i also tried
static def getProperty (String prop) { println "accessed $prop"}
but this isnt called either.
So other than adding to metaClass in my code/script before i use, how can declare the in the utility class that want to capture property accesses
the actual class i have looks like this at present
class UnitOfMeasure {
static ConcurrentLinkedQueue UoMList = new ConcurrentLinkedQueue(["Each", "Per Month", "Days", "Months", "Years", "Hours", "Minutes", "Seconds" ])
String uom
UnitOfMeasure () {
if (!UoMList.contains(this) )
UoMList << this
}
static list () {
UoMList.toArray()
}
static getAt (index) {
def value = null
if (index in 0..(UoMList.size() -1))
value = UoMList[index]
else if (index instanceof String) {
Closure matchClosure = {it.toUpperCase().contains(index.toUpperCase())}
def position = UoMList.findIndexOf (matchClosure)
if (position != -1)
value = UoMList[position]
}
value
}
static def propertyMissing (receiver, String propName) {
println "prop $propName, saught"
}
//expects either a String or your own closure, with String will do case insensitive find
static find (match) {
Closure matchClosure
if (match instanceof Closure)
matchClosure = match
if (match instanceof String) {
matchClosure = {it.toUpperCase().contains(match.toUpperCase())}
}
def inlist = UoMList.find (matchClosure)
}
static findWithIndex (match) {
Closure matchClosure
if (match instanceof Closure)
matchClosure = match
else if (match instanceof String) {
matchClosure = {it.toUpperCase().contains(match.toUpperCase())}
}
def position = UoMList.findIndexOf (matchClosure)
position != -1 ? [UoMList[position], position] : ["Not In List", -1]
}
}
i'd appreciate the secret of doing this for a static utility class rather than instance level property interception, and doing it in class declaration - not by adding to metaClass before i make the calls.
just so you can see the actual class, and script that calls - i've attached these below
my script thats calling the class looks like this
println UnitOfMeasure.list()
def (uom, position) = UnitOfMeasure.findWithIndex ("Day")
println "$uom at postition $position"
// works UnitOfMeasure.metaClass.static.propertyMissing = {name -> println "accessed prop called $name"}
println UnitOfMeasure[4]
println UnitOfMeasure.'Per'
which errors like this
[Each, Per Month, Days, Months, Years, Hours, Minutes, Seconds]
Days at postition 2
Years
Caught: groovy.lang.MissingPropertyException: No such property: Per for class: com.softwood.portfolio.UnitOfMeasure
Possible solutions: uom
groovy.lang.MissingPropertyException: No such property: Per for class: com.softwood.portfolio.UnitOfMeasure
Possible solutions: uom
at com.softwood.scripts.UoMTest.run(UoMTest.groovy:12)
Static version of propertyMissing method is called $static_propertyMissing:
static def $static_propertyMissing(String name) {
// do something
}
This method gets invoked by MetaClassImpl at line 1002:
protected static final String STATIC_METHOD_MISSING = "$static_methodMissing";
protected static final String STATIC_PROPERTY_MISSING = "$static_propertyMissing";
// ...
protected Object invokeStaticMissingProperty(Object instance, String propertyName, Object optionalValue, boolean isGetter) {
MetaClass mc = instance instanceof Class ? registry.getMetaClass((Class) instance) : this;
if (isGetter) {
MetaMethod propertyMissing = mc.getMetaMethod(STATIC_PROPERTY_MISSING, GETTER_MISSING_ARGS);
if (propertyMissing != null) {
return propertyMissing.invoke(instance, new Object[]{propertyName});
}
} else {
// .....
}
// ....
}
Example:
class Hello {
static def $static_propertyMissing(String name) {
println "Hello, $name!"
}
}
Hello.World
Output:
Hello, World!

how to get value generated in mock method

I have a service method in FooService
public void doSomething(){
ArrayList<Foo> fooList = ...;
barService.batchAddFoos(fooList);
List<String> codeList = new ArrayList<>();
for (Foo foo : fooList) {
codeList.add(foo.getCode());
}
String key = "foo_codes";
redisService.sadd(key,codeList.toArray(new String[]{}));
// other code also need use code
}
BarService.batchAddFoos
for (Foo foo : foos) {
foo.setCode(UUID.randomUUID().toString()); // dynamically generate the code value
}
Then I have a unit test to test FooService logic
#Test
public void doSomething() throws Exception {
fooService.doSomething();
ArgumentCaptor<List<Foo>> fooListCaptor = ArgumentCaptor.forClass(List.class);
verify(barService).batchAddFoos(fooListCaptor.capture());
List<Foo> fooList = fooListCaptor.getValue();
Assert.assertNotNull(fooList.get(0).getCode()); // check code value is generated successfully
List<String> codeList = new ArrayList<>();
for (Foo foo : fooList) {
codeList.add(foo.getCode());
}
verify(redisService).sadd("foo_codes",codeList.toArray(new String[]{}));
}
but it is failed, because the code value is null, actually it does not execute any code in BarService.batchAddFoos.I even tried to explicitly populate code value,
fooList.get(0).setCode("aaa");
fooList.get(1).setCode("bbb");
but it is still failed.
Argument(s) are different! Wanted:
redisService.sadd("foo_codes", "aaa", "bbb");
Actual invocation has different arguments:
redisService.sadd("foo_codes", null, null);
Any idea to solve this problem?
Since the fooList is a local variable of the FooService.doSomething, you cannot populate it from the test. Your test will not fail, if assertion will be as following:
Mockito.verify(barService).batchAddFoos(fooListCaptor.capture());
List<Foo> fooList = fooListCaptor.getValue();
//Assert.assertNotNull(fooList.get(0).getCode());
Assert.assertFalse(fooList.isEmpty());
...
If you will initialize the code in the Foo constructor with Strings.EMPTY or any other not null value, your original assertion will work.
As such scenario, could populate some properties of some object parameters as you like, e.g.
doAnswer(new Answer() {
#Override
public Void answer(InvocationOnMock invocationOnMock) throws Throwable {
List<Foo> fooList = invocationOnMock.getArgumentAt(0, List.class);
fooList.get(0).setCode("aaa"); // explicitly specify the first foo object have code of "aaa"
fooList.get(1).setCode("bbb"); // explicitly specify the second foo object have code of "bbb"
return null;
}
}).when(barService).batchAddFoos(anyList());

JSF 2: Using enums in the rendered attribute

Is there any way to check declaratively whether an enum has a specified value. For example:
<h:graphicImage name="error.png" library="images"
rendered="#{viewController.current.status == Status.ERROR}" />
It's a little bit tedious to define a method in the managed beand that checks this for every enum value, e.g.
public boolean isStateIsError() {
return current.getStatus() == Status.ERROR;
}
Is there a shorter/better way of doing this?
Until EL 3.0 it's not possible to import enums in EL scope. You can however just treat and compare them like strings, i.e. the enum constant value must be quoted like below.
<h:graphicImage name="error.png" library="images"
rendered="#{viewController.current.status eq 'ERROR'}" />
I know this question is a bit older now, but i had the same problem and found another solution, which i want to share :
Create a Custom EL-Resolver and use enums and java constants as objects in jsf el:
<h:graphicImage name="error.png" library="images"
rendered="#{viewController.current.status == Status.ERROR}" />
But before you can use enums this way you have to do 3 steps.
1. step - Copy this Class and replace "MY_ENUM" through your enumClass (in the example above it would be "Status")
public class EnumCache {
private Map<String, Object> propertCache = new HashMap<String, Object>();
private Map<String, Class> baseCache = new HashMap<String, Class>();
private static EnumCache staticEnumCache = null;
public static EnumCache instance() {
if (staticEnumCache == null) { staticEnumCache = new EnumCache(); }
return staticEnumCache;
}
private EnumCache() {
List<Class<?>> classes = new ArrayList<Class<?>>();
classes.add(MY_ENUM.class);
for(Class clazz : classes) {
try {
baseCache.put(clazz.getSimpleName(), clazz);
Method m = clazz.getMethod("values", (Class[]) null);
Enum<?>[] valueList = (Enum[]) m.invoke(null, (Object[]) null);
for (Enum<?> en : valueList) {
propertCache.put(clazz.getSimpleName() + "." + en.name(), en);
}
} catch (Exception e) {
System.err.println(clazz.getSimpleName(), e);
}
}
}
public Object getValueForKey(String key) {
return propertCache.get(key);
}
public Class getClassForKey(String key) {
return baseCache.get(key);
}
}
2. step - add this EnumResolver - This class will map your JSF expression to the enum in cache (step 1)
public class MyEnumResolver extends ELResolver {
public Object getValue(ELContext context, Object base, Object property) {
Object result = null;
if (base == null) {
result = EnumCache.instance().getClassForKey(property + "");
} else if (base instanceof Class) {
result = EnumCache.instance().getValueForKey(((Class) base).getSimpleName() + "." + property);
}
if (result != null) {
context.setPropertyResolved(true);
}
return result;
}
public Class<?> getCommonPropertyType(ELContext context, Object base) {
return null;
}
public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
return null;
}
public Class<?> getType(ELContext context, Object base, Object property) {
return null;
}
public boolean isReadOnly(ELContext context, Object base, Object property) {
return false;
}
public void setValue(ELContext context, Object base, Object property, Object arg3) {
}
}
3. step - register the EnumResolver in faces-config.xml
<faces-config>
<application>
<el-resolver>com.asd.MyEnumResolver</el-resolver>
</application>
</faces-config>
NOTE:
If you want to access your java constants this way, you just have to extend the constructor of the enumCache class.
This (untestet) example should work:
baseCache.put(CLASS_WITH_CONSTANTS.getSimpleName(), clazz);
for (Field field : CLASS_WITH_CONSTANTS.getDeclaredFields()) {
try {
propertCache.put(CLASS_WITH_CONSTANTS.getSimpleName() + "."
+ field.getName(), field.get(null));
} catch (Exception e) { }
}
Hope this reduced but working code can help anybody.
Update
I see this benefits:
If you use strings in jsf (viewController.current.status == 'ERROR_abcdefg'), you can misspell the value and wont recognise it so fast.
With my solution you would get an error while loading the jsf file, because the enum could not be resolved.
You can see in the sourcecode that "ERROR" is value of the enum "STATUS".
When you compare two values in el, the class of the enums will be compared too.
So for example PersonState.ACTIV is not the same like AccounState.ACTIV.
When i have to change my enum value from PersonState.ACTIV to PersonState.ACTIVATED i can search for the String "PersonState.ACTIV" in my sourcecode. searching for "ACTIV" would have much more matches.
I solved a similar problem by statically dumping all the enum keys (which are used in the rendered UI components) in a map and then I use a static getByKey method to convert the value from the UI into an actual native enum in the setter, throwing an Exception if the value provided is invalid:
public enum ReportType {
FILING("F", "Filings"),
RESOLUTION("R", "Resolutions"),
BASIS("B", "Bases"),
STAFF("T", "Staff Counts"),
COUNTS("I", "Counts");
private String key;
private String label;
private static Map<String, ReportType> keyMap = new HashMap<String, ReportType>();
static {
for(ReportType type : ReportType.values()) {
keyMap.put(type.getKey(), type);
}
}
private ReportType(String _key, String _label) {
this.key = _key;
this.label = _label;
}
public String getKey() {
return this.key;
}
public String getLabel() {
return this.label;
}
public static List<ReportType> getValueList() {
return Arrays.asList(ReportType.values());
}
public static ReportType getByKey(String _key) {
ReportType result = keyMap.get(_key);
if(result == null) {
throw new IllegalArgumentException("Invalid report type key: " + _key);
}
return result;
}
}
In the UI tier, the enum key is used as the value and the enum label is used as the label:
<f:selectItems var="rptTypeItem" value="#{reportController.allReportTypes}"
itemLabel="#{rptTypeItem.label}" itemValue="#{rptTypeItem.key}"/>
In the managed bean, I convert the enum into a renderable list, using the getValueList() from the enum:
public List<ReportType> getAllReportTypes() {
return ReportType.getValueList();
}
Finally, the [g|s]etters in the managed bean look as follows:
public String getReportType() {
return this.crtRptType.getKey();
}
public void setReportType(String _val) {
this.crtRptType = ReportType.getByKey(_val);
}
I think it could be done it the following way:
Create a method in you bean that would return the list of enums, for example
public Status[] getStatuses() {
Status.values();
}
then you can use the enum in EL like this
<h:graphicImage name="error.png" library="images"
rendered="#{viewController.current.status == someBean.statuses[0]}" />
assuming that the order of enum members is not going to be changed (for ex. here statuses[0] is ERROR). However, I would fix the positions like this:
public Status[] getStatuses() {
Status myStatuses = new Status [2]; // or whatever number of statuses you are going to use in UI
myStatuses [0] = Status.ERROR;
myStatuses [1] = Status.RUNNING;
return myStatuses;
}
This is still not dynamic solution, but it's better than hard-coding in EL. Might be especially useful when you'r using localization for you statuses (enum values depending on locale/translation).

Resources