Hoang Nguyen huu
Hoang Nguyen huu

Reputation: 87

How to Load Values for Java Enum Elements from A File

I have a Java Enum:

public enum CodeType {
  BRONZE("00001BP", "BAP"),
  SILVER("00002SL", "SAP"),
  GOLD("00003GL", "GAP"),
  MOBILE("00004MB", "TCM"),
  SOCIAL("00005SM", "ASM"),
  WEB_PRESENCE("00006WP", "GLO"),
  EMAIL_MARKETING("00007EM", "PEM"),
  CUSTOM_DIAMOND("00008CD", "PCS"),
  CONSUMER_PORTAL("00009CP", "CPS");

  private String code;
  private String key;

  CodeType(String code, String key) {
    this.code = code;
    this.key = key;
  }

  ...
}

As you see, I have nine elements and each has two values. My question is How can I load values for those elements from a file like properties or xml? I mean:

BRONZE(isLoadedFromFile, isLoadedFromFile),
...
CONSUMER_PORTAL(isLoadedFromFile, isLoadedFromFile);

Thanks so much.

Upvotes: 2

Views: 9753

Answers (3)

Joshua Goldberg
Joshua Goldberg

Reputation: 5333

One option is to generate a static map based on the resource file within the enum class, mapping from enum values to the data in the file. The map can then be used for the getter.

For instance with a resource file formatted like this:

A=red
B=blue
C=yellow

it can be initialized like this:

public enum MyEnum {
    A, B, C;
    
    public String getFoo() {
        return enumFooValuesFromResourceFile.get(this);
    }

    private static final Map<MyEnum, String> enumFooValuesFromResourceFile;
    static {
        Map<MyEnum, String> temp = Collections.emptyMap();
        try {
            String data = new String(MyEnum.class.getResourceAsStream("resourcepath").readAllBytes());
            temp = Arrays.stream(data.split("\n"))
                    .map(line -> line.split("="))
                    .collect(Collectors.<String[], MyEnum, String>toMap(
                            key_val -> MyEnum.valueOf(key_val[0]),
                            key_val -> key_val[1]));
        } catch (IOException iE) {
            // helpful message.
        } finally { enumFooValuesFromResourceFile = temp; }
    }
}

A nicer option, I think, is to use a static String for the resource file data, and store the values directly on the enum items during initialization. During enum initialization, you cannot access a static property of the enum, so it must either be outside it, or in an inner class using the Initialization-on-demand holder idiom (credit to) which is neat, because it's lazy and not loaded if the enum is never accessed.

(I found I can set the (non-final) String to null at the end of the enum declaration, freeing that memory.)

public enum MyEnum {
    A, B, C;

    public String getFoo() { return foo; }

    final String foo;

    MyEnum() {
        foo = getFooValue();
    }

    private String getFooValue() {
        return Arrays.stream(ResourceHolder.resourceFileString.split("\n"))
                .filter(str -> str.startsWith(this.name() + '='))
                .findFirst()
                .map(str -> str.replaceAll("^" + this.name() + '=', ""))
                .orElseThrow(() ->
                        new IllegalArgumentException(this.name() + " not found in resourcefile."));
    }
    // Release resources (string) from memory after enum initialization.
    static {ResourceHolder.resourceFileString = null;}

    private static class ResourceHolder {
        // Lazily initialized if/when MyEnum is accessed.
        // Cleared after initialization.
        private static String resourceFileString;
        static {
            try {
                InputStream lResource =
                        Objects.requireNonNull(MyEnum.class.getResourceAsStream("resourcepath"));
                resourceFileString = new String(lResource.readAllBytes());
            } catch (IOException iE) {
                // helpful message.
                iE.printStackTrace();
            }
        }

    }

}

Upvotes: 0

Sinto
Sinto

Reputation: 930

Try something like this..

public enum EnumTest {

    BRONZE, SILVER;

    public String getProperty(String keyOrCode) {
        Properties prop = new Properties();
        try {
            prop.load(new FileInputStream("E:\\EnumMapper.properties"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return prop.getProperty(this.name() + "." + keyOrCode);
    }

    public String getCode() {
        return getProperty("CODE");
    }

    public String getKey() {
        return getProperty("KEY");
    }

    public static void main(String[] args) {
        System.out.println(EnumTest.BRONZE.getCode());
        System.out.println(EnumTest.BRONZE.getKey());

    }

}

where the EnumMapper.properties contains

BRONZE.CODE=00001BP
BRONZE.KEY=BAP
SILVER.CODE=00002SL
SILVER.KEY=SAP

Just wanted to share some possibilities..

Upvotes: 11

Brian Roach
Brian Roach

Reputation: 76908

If I understand your question correctly, you would need to do so in the constructor (which is misnamed in your example).

The hard-coded defaults you show would serve as the defaults, but in the constructor you would check/load some properties file and override them.

In general though, this smells of an odd/bad design. You would need to hard-code that properties file / resource in the enum. You're also dynamically loading what is meant to be something that represents a constant value.

It seems like really you should be using your own class to hold these values.

Upvotes: 0

Related Questions