reflection - AspectJ - Is is possible to extend an enum's value? -


say have enum

public enum e {a,b,c} 

is possible add value, d, aspectj?

after googling around, seems there used way hack private static field $values, call constructor(string, int) reflection, seems not working 1.7 anymore.

here several links: http://www.javaspecialists.eu/archive/issue161.html (provided @wimdeblauwe )

and this: http://www.jroller.com/velkavrana/entry/modify_enum_with_reflection

actually, recommend refactor source code, maybe adding collection of valid region ids each enumeration value. should straightforward enough subsequent merging if use git , not old-school scm tool svn.

maybe make sense use dynamic data structure altogether instead of enum if clear in future list of commands dynamic. should go upstream code base. sure devs accept patch or pull request if prepared cleanly.

remember: trying avoid refactoring bad smell, symptom of illness, not solution. prefer solutions symptomatic workarounds. clean code rules , software craftsmanship attitude demand that.


having said above, here can do. should work under jdk 7/8 , found on jérôme kehrli's blog (please sure add bugfix mentioned in 1 of comments below article).

enum extender utility:

package de.scrum_master.util;  import java.lang.reflect.accessibleobject; import java.lang.reflect.array; import java.lang.reflect.field; import java.lang.reflect.modifier; import java.util.arraylist; import java.util.arrays; import java.util.list;  import sun.reflect.constructoraccessor; import sun.reflect.fieldaccessor; import sun.reflect.reflectionfactory;  public class dynamicenumextender {      private static reflectionfactory reflectionfactory =         reflectionfactory.getreflectionfactory();      private static void setfailsafefieldvalue(field field, object target, object value)         throws nosuchfieldexception, illegalaccessexception     {         // let's make field accessible         field.setaccessible(true);          // next change modifier in field instance         // not final anymore, tricking reflection         // letting modify static final field         field modifiersfield = field.class.getdeclaredfield("modifiers");         modifiersfield.setaccessible(true);         int modifiers = modifiersfield.getint(field);          // blank out final bit in modifiers int         modifiers &= ~modifier.final;         modifiersfield.setint(field, modifiers);          fieldaccessor fa = reflectionfactory.newfieldaccessor(field, false);         fa.set(target, value);     }      private static void blankfield(class<?> enumclass, string fieldname)         throws nosuchfieldexception, illegalaccessexception     {         (field field : class.class.getdeclaredfields()) {             if (field.getname().contains(fieldname)) {                 accessibleobject.setaccessible(new field[] { field }, true);                 setfailsafefieldvalue(field, enumclass, null);                 break;             }         }     }      private static void cleanenumcache(class<?> enumclass)         throws nosuchfieldexception, illegalaccessexception     {         blankfield(enumclass, "enumconstantdirectory"); // sun (oracle?!?) jdk 1.5/6         blankfield(enumclass, "enumconstants"); // ibm jdk     }      private static constructoraccessor getconstructoraccessor(class<?> enumclass, class<?>[] additionalparametertypes)         throws nosuchmethodexception     {         class<?>[] parametertypes = new class[additionalparametertypes.length + 2];         parametertypes[0] = string.class;         parametertypes[1] = int.class;         system.arraycopy(additionalparametertypes, 0, parametertypes, 2, additionalparametertypes.length);         return reflectionfactory.newconstructoraccessor(enumclass .getdeclaredconstructor(parametertypes));     }      private static object makeenum(class<?> enumclass, string value, int ordinal, class<?>[] additionaltypes, object[] additionalvalues)         throws exception     {         object[] parms = new object[additionalvalues.length + 2];         parms[0] = value;         parms[1] = integer.valueof(ordinal);         system.arraycopy(additionalvalues, 0, parms, 2, additionalvalues.length);         return enumclass.cast(getconstructoraccessor(enumclass, additionaltypes).newinstance(parms));     }      /**      * add enum instance enum class given argument      *      * @param <t> type of enum (implicit)      * @param enumtype class of enum modified      * @param enumname name of new enum instance added class      */     @suppresswarnings("unchecked")     public static <t extends enum<?>> void addenum(class<t> enumtype, string enumname) {         // 0. sanity checks         if (!enum.class.isassignablefrom(enumtype))             throw new runtimeexception("class " + enumtype + " not instance of enum");          // 1. lookup "$values" holder in enum class , previous enum         // instances         field valuesfield = null;         field[] fields = enumtype.getdeclaredfields();         (field field : fields) {             if (field.getname().contains("$values")) {                 valuesfield = field;                 break;             }         }         accessibleobject.setaccessible(new field[] { valuesfield }, true);          try {              // 2. copy             t[] previousvalues = (t[]) valuesfield.get(enumtype);             list<t> values = new arraylist<t>(arrays.aslist(previousvalues));              // 3. build new enum             t newvalue = (t) makeenum(                 enumtype,                         // target enum class                 enumname,                         // new enum instance dynamically added                 values.size(), new class<?>[] {}, // used pass values enum constuctor if needed                 new object[] {}                   // used pass values enum constuctor if needed             );              // 4. add new value             values.add(newvalue);              // 5. set new values field             setfailsafefieldvalue(valuesfield, null, values.toarray((t[]) array.newinstance(enumtype, 0)));              // 6. clean enum cache             cleanenumcache(enumtype);          } catch (exception e) {             e.printstacktrace();             throw new runtimeexception(e.getmessage(), e);         }     } } 

sample application & enum:

package de.scrum_master.app;  /** in honour of "the secret of monkey island"... ;-) */ public enum command {     open, close, push, pull, walk_to, pick_up, talk_to, give, use, look_at, turn_on, turn_off } 
package de.scrum_master.app;  public class server {     public void executecommand(command command) {         system.out.println("executing command " + command);     } } 
package de.scrum_master.app;  public class client {     private server server;      public client(server server) {         this.server = server;     }      public void issuecommand(string command) {         server.executecommand(             command.valueof(                 command.touppercase().replace(' ', '_')             )         );     }      public static void main(string[] args) {         client client = new client(new server());         client.issuecommand("use");         client.issuecommand("walk to");         client.issuecommand("undress");         client.issuecommand("sleep");     } } 

console output original enum:

executing command use executing command walk_to exception in thread "main" java.lang.illegalargumentexception: no enum constant de.scrum_master.app.command.undress     @ java.lang.enum.valueof(enum.java:236)     @ de.scrum_master.app.command.valueof(command.java:1)     @ de.scrum_master.app.client.issuecommand(client.java:12)     @ de.scrum_master.app.client.main(client.java:22) 

now can either add aspect advice executed after enum class loaded or call manually in application before extended enum values used first time. here showing how can done in aspect.

enum extender aspect:

package de.scrum_master.aspect;  import de.scrum_master.app.command; import de.scrum_master.util.dynamicenumextender;  public aspect commandextender {     after() : staticinitialization(command) {         system.out.println(thisjoinpoint);         dynamicenumextender.addenum(command.class, "undress");         dynamicenumextender.addenum(command.class, "sleep");         dynamicenumextender.addenum(command.class, "wake_up");         dynamicenumextender.addenum(command.class, "dress");     } } 

console output extended enum:

staticinitialization(de.scrum_master.app.command.<clinit>) executing command use executing command walk_to executing command undress executing command sleep 

et voilà! ;-)


Comments

Popular posts from this blog

javascript - Jquery show_hide, what to add in order to make the page scroll to the bottom of the hidden field once button is clicked -

javascript - Highcharts multi-color line -

javascript - Enter key does not work in search box -