views:

86

answers:

4

I created

enum Restrictions{
  none,
  enumeration,
  fractionDigits,
  length,
  maxExclusive,
  maxInclusive,
  maxLength,
  minExclusive,
  minInclusive,
  minLength,
  pattern,
  totalDigits,
  whiteSpace;

  public Restrictions setValue(int value){
    this.value = value;
    return this;
  }
  public int value;
}

So that I could happily do something like this, which is perfectly legal syntax.

Restrictions r1 =
  Restrictions.maxLength.setValue(64);

The reason being is, I am using enum to restrict the type of restriction that could be used, and be able to assign a value to that restriction.

However, my actual motivation is to use that restriction in an @annotation.

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
public @interface Presentable {
  Restrictions[] restrictions() default Restrictions.none;
}

So that, I intended to do this:

@Presentable(restrictions=Restrictions.maxLength.setValue(64))
public String userName;

to which, the compiler croaks

The value for annotation enum attribute must be an enum constant expression.

Is there a way to accomplish what I wish to accomplish

+1  A: 

You can achieve what you want, but not with enums directly.

If you make Restriction a regular class, with private constructor and static constant fields, you can then use method chaining to create new instances fluently:

enum RestrictionType
{
   none,
   enumeration,
   maximumLength, 
   // ... etc.
}

class Restriction {
  static public final Restriction none = new Restriction(RestrictionType.none);
  static public final Restriction enumeration = new Restriction(RestrictionType.enumeration);
  static public final Restriction maximumLength = new Restriction(RestrictionType.maximumLength);

  ... etc

  RestrictionType type;
  int value;

  private Restriction(RestrictionType type)
  {
    this(type, 0);
  }
  private Restriction(RestrictionType type, int value)
  {
     this.type = type;
     this.value = value; // you don't really need
  }

  static public Restriction setValue(int value)
  {
      return new Restriction(type, value);
  }
}

Which is then used exactly as your original code:

@Presentable(restrictions=Restrictions.maxLength.setValue(64))
public String userName;

However, I'm concerned for the lack of OO here - if the restrictions have different behaviour or data needed for definition, then you will end up lumping everything in the Restrictions class. It will be better to create subclasses for the different restriction types.

mdma
Dont' you think you are creating a new instance on every invocation of set method?
Adeel Ansari
public @interface Presentable {Restriction[] restrictions() default Restriction.none;} results in compiler croaking "only primitive types, String, Class, annotation or enum, or 1 dim arrays thereof, are permitted."
Blessed Geek
@h2g2java Make `Restriction` an annotation type.
mdma
@Adeel - yes this creates a new instance for each annotation. The annotations have state, and so separate instances are required. How could you do it without creating a new instance?
mdma
@mdma: I agree that its having state.
Adeel Ansari
+1  A: 

A part from compilation error, suppose you are able to do exactly this. Then don't you think applying the similar annotation on some other field will ruin the first one?

I mean to say,

@Presentable(restrictions=Restrictions.maxLength.setValue(64))
public String userName;
@Presentable(restrictions=Restrictions.maxLength.setValue(32))
public String password;

The same instance will now have a different value, that is 32. So, the 64 will be lost, I believe. In case, they are processed on runtime sequentially, and at the time we change the value to 32, 64 one has already been processed. Then I suppose, we can change the setter method in the example given by mdma to something like below.

 static public Restriction setValue(int value) {    
      this.value = value;
      return this;
  }
Adeel Ansari
+3  A: 

You can do it like this:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

class Person {    
    @Presentable({
        @Restriction(type = RestrictionType.LENGTH, value = 5),
        @Restriction(type = RestrictionType.FRACTION_DIGIT, value = 2)
    })
    public String name;
}

enum RestrictionType {
    NONE, LENGTH, FRACTION_DIGIT;
}

@Retention(RetentionPolicy.RUNTIME)
@interface Restriction {
    RestrictionType type() default RestrictionType.NONE;
    int value() default 0;
}

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@interface Presentable {
  Restriction[] value();
}
abhin4v
Indeed, a better approach. Couldn't see this in the first place, while thinking along the same line. +1
Adeel Ansari
A: 

I chose Abhin's as the answer to my question because it was the most comprehensive and it worked when I tried it out. However, I document here, in the form of an answer to my own question, what I actually did.

Renaming Abhin's terms, this would be how I would apply it (similar to Abhin's example):

@Presentable({
@Restrictions(restriction=Restriction.FractionDigits, value="1"),
@Restrictions(restriction=Restriction.Length, value="10"),
    .....
})

Which I decided is too verbose. I could even abridge it to:

@Presentable({
@R(r=R.FractionDigits, v="1"),
@R(r=R.Length, v="10"),
    .....
})

Which might be too incomprehensible and still to verbose. What I needed was something a programmer could specify quickly and comprehensively:

@Presentable(sequence = 11, maxLen=64, readOnly=true)

Therefore, I decided to use the quick and dirty:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
public @interface Presentable {
    int sequence();
    String caption() default "";
    int fractionDigits() default -1;
    int length() default -1;
    int maxLen() default -1;
    int minLen() default -1;
    int totalDigits() default -1;
    float maxVal() default -1;
    float minVal() default -1;
    String pattern() default "";
    String whiteSpace() default "";
    boolean readOnly() default false;
    boolean multiValue() default false;
    boolean hidden() default false;
    boolean isTest() default true;
}

Anyway, I am keeping Abhin's answer in my recesses for future use.

Blessed Geek