Solved Unexpected Error

Discussion in 'Spigot Plugin Development' started by ExpDev, May 7, 2017.

  1. Hey,

    I am trying to use common's EnumUtils as following:
    Code (Text):
    if (EnumUtils.getEnum(TL.class, tlPath) != null) {
    However, it is giving me a "Caused by: java.lang.IllegalArgumentException: The Class must be a subclass of Enum". Although TL clearly is an Enum:

    Code (Text):
    public enum TL {

        /**
         * Translation meta
         */
        _AUTHOR("misc"),
        _RESPONSIBLE("misc"),
        _LANGUAGE("English"),
        _ENCODING("UTF-8"),
        _LOCALE("en_US"),
        _REQUIRESUNICODE("false"),
        _DEFAULT("true"),
        _STATE("complete"), //incomplete, limited, partial, majority, complete

        /**
         * Localised translation meta
         */
        _LOCAL_AUTHOR("misc"),
        _LOCAL_RESPONSIBLE("misc"),
        _LOCAL_LANGUAGE("English"),
        _LOCAL_REGION("US"),
        _LOCAL_STATE("complete"), //And this is the English version. It's not ever going to be not complete.

        /**
         * Command translations
         */
        COMMAND_SETSPAWN("&aYou set spawn in \"&c%1$s&a\" @ &cx%2$s, z%3$s, y%4$s"),
        COMMAND_SPAWN("&aYou have been teleported to spawn"),
        COMMAND_SETPOINT("&aYou set a point (&cid: %1$s&a) in \"&c%2$s&a\" @ &cx%3$s, z%4$s, y%5$s"),
        COMMAND_REMOVEPOINT("&aYou removed a point (&cid: %1$s&a)");

      TL(String path, String start) {
          this.path = path;
          this.def = start;
      }

      @Override
      public String toString() {
          return this == TITLE ? ChatColor.translateAlternateColorCodes('&', LANG.getString(this.path, def)) + " " : ChatColor.translateAlternateColorCodes('&', LANG.getString(this.path, def));
      }
    }
    EDIT: So confused
    I even tried:
    Code (Text):
    public enum  MEnum {

        YES,
        THIS,
        IS,
        ENUM

    }
    Code (Text):
    String s = "YES";
    System.out.println(EnumUtils.getEnum(MEnum.class, s) == null);
    However, it still threw the exception: java.lang.IllegalArgumentException: The Class must be a subclass of Enum

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

    EDIT2: After checking the source, this appears to be throwing the error:
    Code (Text):
    static Enum getEnum(Class enumClass, String name) {
        Entry entry = getEntry(enumClass);
        if (entry == null) {
            return null;
        }
        return (Enum) entry.map.get(name);
    }
    Code (Text):
    private static Entry getEntry(Class enumClass) {
        if (enumClass == null) {
            throw new IllegalArgumentException("The Enum Class must not be null");
        }
        if (Enum.class.isAssignableFrom(enumClass) == false) {
            throw new IllegalArgumentException("The Class must be a subclass of Enum");
        }
        Entry entry = (Entry) cEnumClasses.get(enumClass);

        if (entry == null) {
            try {
                // LANG-76 - try to force class initialization for JDK 1.5+
                Class.forName(enumClass.getName(), true, enumClass.getClassLoader());
                entry = (Entry) cEnumClasses.get(enumClass);
            } catch (Exception e) {
                // Ignore
            }
        }

        return entry;
    }
    The important part here is obviously:
    Code (Text):
    if (Enum.class.isAssignableFrom(enumClass) == false) {
        throw new IllegalArgumentException("The Class must be a subclass of Enum");
    }
    So, the issue is: the enum classes I am creating is not isAssignableFrom my enum class. Which is weird? Does anyone here know why it is not assignable? Or what the assignable method runs?

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

    EDIT3: However, running
    Code (Text):
    System.out.println(Enum.class.isAssignableFrom(TL.class));
    returns true.

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

    EDIT4:
    Code (Text):
    System.out.println(Enum.class.isAssignableFrom(TL.class));
    System.out.println(EnumUtils.getEnum(TL.class, "yes") == null);
    Output ???!?!?!?!?!?:
    Code (Text):
    true
    Exception in thread "main" java.lang.IllegalArgumentException: The Class must be a subclass of Enum
        at org.apache.commons.lang.enums.Enum.getEntry(Enum.java:530)
        at org.apache.commons.lang.enums.Enum.getEnum(Enum.java:450)
        at org.apache.commons.lang.enums.EnumUtils.getEnum(EnumUtils.java:52)
        at TestTest.main(TestTest.java:30)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
    This absolutely makes no sense.
     
    #1 ExpDev, May 7, 2017
    Last edited: May 9, 2017
  2. What is tlPath?
     
  3. That would be:
    String tlPath = "DIED_NATURAL_" + cause.toString().toUpperCase();

    However, it should be irrelevant as according to the documentation, if the path does not exist, it should return null which I do have a check for. Looks like the problem is that the subclass (TL?) is not an Enum, which as you can see, it obviously is [public enum TL {)
     
  4. The constructor of TL has two arguments, but you have only one argument per enum?

    _AUTHOR("misc"),

    Should be

    _AUTHOR("misc", "Something here"),

    Or change your constructor.
     
    • Winner Winner x 1
  5. Well, it has two constructors:

    Code (Text):
    /**
    * Lang enum constructor.
    *
    * @param path  The string path.
    * @param start The default string.
    */
    TL(String path, String start) {
        this.path = path;
        this.def = start;
    }

    /**
    * Lang enum constructor. Use this when your desired path simply exchanges '_' for '.'
    *
    * @param start The default string.
    */
    TL(String start) {
        this.path = this.name().replace('_', '.');
        if (this.path.startsWith(".")) {
            path = "root" + path;
        }
        this.def = start;
    }
    However, again should be irrelevant as that has nothing to do with the path existing or not, does it?
     
  6. Oh right, I was just looking at the code and figured that shouldn't even compile. I would almost think you have the wrong import of TL, could you verify that?
     
  7. import me.expdev.gkitpvp.utils.translation.TL;

    TL:
    package me.expdev.gkitpvp.utils.translation;

    = Nope.

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

    EDIT:

    I tried
    Code (Text):
    System.out.println(EnumUtils.getEnumList(TL.class));
    And to my surprise, it did not work:
    Code (Text):
    Caused by: java.lang.IllegalArgumentException: The Class must be a subclass of Enum
            at org.apache.commons.lang.enums.Enum.getEntry(Enum.java:530) ~[spigot-1.11.2.jar:git-Spigot-b32c8f8-4d3bf20]
            at org.apache.commons.lang.enums.Enum.getEnumList(Enum.java:493) ~[spigot-1.11.2.jar:git-Spigot-b32c8f8-4d3bf20]
            at org.apache.commons.lang.enums.EnumUtils.getEnumList(EnumUtils.java:101) ~[spigot-1.11.2.jar:git-Spigot-b32c8f8-4d3bf20]

    EDIT: So confused
    I even tried:
    Code (Text):
    public enum  MEnum {

        YES,
        THIS,
        IS,
        ENUM

    }
    Code (Text):
    String s = "YES";
    System.out.println(EnumUtils.getEnum(MEnum.class, s) == null);
    However, it still threw the exception: java.lang.IllegalArgumentException: The Class must be a subclass of Enum

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

    EDIT2: After checking the source, this appears to be throwing the error:
    Code (Text):
    static Enum getEnum(Class enumClass, String name) {
        Entry entry = getEntry(enumClass);
        if (entry == null) {
            return null;
        }
        return (Enum) entry.map.get(name);
    }
    Code (Text):
    private static Entry getEntry(Class enumClass) {
        if (enumClass == null) {
            throw new IllegalArgumentException("The Enum Class must not be null");
        }
        if (Enum.class.isAssignableFrom(enumClass) == false) {
            throw new IllegalArgumentException("The Class must be a subclass of Enum");
        }
        Entry entry = (Entry) cEnumClasses.get(enumClass);

        if (entry == null) {
            try {
                // LANG-76 - try to force class initialization for JDK 1.5+
                Class.forName(enumClass.getName(), true, enumClass.getClassLoader());
                entry = (Entry) cEnumClasses.get(enumClass);
            } catch (Exception e) {
                // Ignore
            }
        }

        return entry;
    }
    The important part here is obviously:
    Code (Text):
    if (Enum.class.isAssignableFrom(enumClass) == false) {
        throw new IllegalArgumentException("The Class must be a subclass of Enum");
    }
    So, the issue is: the enum classes I am creating is not isAssignableFrom my enum class. Which is weird? Does anyone here know why it is not assignable? Or what the assignable method runs?

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

    EDIT3: However, running
    Code (Text):
    System.out.println(Enum.class.isAssignableFrom(TL.class));
    returns true.
     
    #7 ExpDev, May 7, 2017
    Last edited: May 9, 2017
  8. Well, bump.
     
  9. Print your class to the console, as well as the string output you are passing.
     
  10. Just brainstorming here, but is it possible it is looking for a subclass of org.apache.commons.lang.enums.Enum?

    EDIT: Checked the docs, I was mistaken. It is (And should be) the java.lang.Enum
     
  11. Do you mean Class#toString()? The string out put I printed earlier and should be correct.
     
  12. I even tried:
    Code (Text):
    public enum  MEnum {

        YES,
        THIS,
        IS,
        ENUM

    }
    Code (Text):
    String s = "YES";
    System.out.println(EnumUtils.getEnum(MEnum.class, s) == null);
    However, it still threw the exception: java.lang.IllegalArgumentException: The Class must be a subclass of Enum


    EDIT: After checking the source, this appears to be throwing the error:
    Code (Text):
    static Enum getEnum(Class enumClass, String name) {
        Entry entry = getEntry(enumClass);
        if (entry == null) {
            return null;
        }
        return (Enum) entry.map.get(name);
    }
    Code (Text):
    private static Entry getEntry(Class enumClass) {
        if (enumClass == null) {
            throw new IllegalArgumentException("The Enum Class must not be null");
        }
        if (Enum.class.isAssignableFrom(enumClass) == false) {
            throw new IllegalArgumentException("The Class must be a subclass of Enum");
        }
        Entry entry = (Entry) cEnumClasses.get(enumClass);

        if (entry == null) {
            try {
                // LANG-76 - try to force class initialization for JDK 1.5+
                Class.forName(enumClass.getName(), true, enumClass.getClassLoader());
                entry = (Entry) cEnumClasses.get(enumClass);
            } catch (Exception e) {
                // Ignore
            }
        }

        return entry;
    }
    The important part here is obviously:
    Code (Text):
    if (Enum.class.isAssignableFrom(enumClass) == false) {
        throw new IllegalArgumentException("The Class must be a subclass of Enum");
    }
    So, the issue is: the enum classes I am creating is not isAssignableFrom my enum class. Which is weird? Does anyone here know why it is not assignable? Or what the assignable method runs?

    EDIT2:
    However, running
    Code (Text):
    System.out.println(Enum.class.isAssignableFrom(TL.class));
    returns true.

    EDIT3:
    Code (Text):
    System.out.println(Enum.class.isAssignableFrom(TL.class));
    System.out.println(EnumUtils.getEnum(TL.class, "yes") == null);
    Output ???!?!?!?!?!?:
    Code (Text):
    true
    Exception in thread "main" java.lang.IllegalArgumentException: The Class must be a subclass of Enum
        at org.apache.commons.lang.enums.Enum.getEntry(Enum.java:530)
        at org.apache.commons.lang.enums.Enum.getEnum(Enum.java:450)
        at org.apache.commons.lang.enums.EnumUtils.getEnum(EnumUtils.java:52)
        at TestTest.main(TestTest.java:30)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
    This absolutely makes no sense.
     
    #13 ExpDev, May 9, 2017
    Last edited: May 9, 2017
  13. Code (Text):
    } else if(!(class$org$apache$commons$lang$enums$Enum == null?(class$org$apache$commons$lang$enums$Enum = class$("org.apache.commons.lang.enums.Enum")):class$org$apache$commons$lang$enums$Enum).isAssignableFrom(enumClass)) {
                throw new IllegalArgumentException("The Class must be a subclass of Enum");
            } else {
    Decompiling through IntelliJ gave this, which tells us the problem. Apache commons uses it's own enum class...

    Is there any reason you cant just do what you are trying to do manually, without EnumUtils? You could use the enum #valueOf, however that would throw an error if there is no value corresponding to exactly the input you gave.

    This 1 liner is quite useful in situations where a player enters an argument which you convert to an enum value:
    Code (Text):
    Arrays.stream(values()).filter(value -> value.name().equalsIgnoreCase(yourString)).findFirst().orElse(null);
     
    • Informative Informative x 1
  14. That is kind of odd. I swear I have used it before, and it has worked. Actually just a few weeks ago.

    Thank you. Marking thread as solved now.
     
  15. Well, nooooot quite.
    https://commons.apache.org/proper/c...i-3.4/org/apache/commons/lang3/EnumUtils.html

    Yes, before apache commons 3 there is an enum system in place which has existed longer than most members on this forum, and should not be used. EnumUtils in lang3 will just use java's enums.

    So either iterate the enum values and match the name yourself or use #valueOf (and catch the exception). Or just update your apache commons to a version that doesn't have 15-20 year old legacy code in numerous places.

    Yeah, it's because you're using an old version (commons 2 vs commons 3)
     
  16. Well, using maven for most recent spigot API build?
     
  17. Yeah, if you wanted to go that route then just get commons3 yourself as a dependency, and shade it into the jar. This is a little overkill for an enum check, though.
     
  18. It is a massive overkill. So guessing that I will just reproduce the method myself.
     
  19. Of course, I am aware, but I just checked for the class which you can see in his stack trace (Which is not from lang3, but lang.enums). Not sure which version Spigot has, as I have added commons for other things anyway.