Generate enum from CSV file

763 Views Asked by At

My goal is to to generate an enum in order to create an EnumSet. This part works if I write the enum. Instead of writing an enum of 50000 lines, I would like to import the 50000 rows CSV. The trouble starts when I try to replace the enum I wrote by the the CSV import.

package VilleEnumSet;

public class VilleEnumSet {
    
    
    //ENUM SANS CSV
    enum Ville {
        Paris("FR", "75000", "Paris", "Ile de France", "Paris", "75", "Paris", "751", "48.8534", "2.3488"),
        Lyon("FR", "69000", "Lyon", "Auvergne-Rhone-Alpes", "Rhone", "69", "Lyon", "691", "45.7485", "4.8467"),
        Marseille("FR", "13000", "Marseille", "Provence-Alpes-Cote d'Azure", "Bouches-du-Rhone", "13", "Marseille", "133", "43.2969", "5.3811"),
        Montpellier("FR", "34000", "Montpellier", "Occitanie", "Herault", "34", "Montpellier", "343", "43.6109", "3.8764");
    
        
    //ATTRIBUTS
    private final String pays;
    private final String codePostal;
    private final String nom;
    private final String region;
    private final String departement;
    private final String numDepartement;
    private final String prefecture;
    private final String codeCommune;
    private final String latitude;
    private final String longitude;
    
    

    //CONSTRUCTEUR
    private Ville(String pays, String codePostal, String nom, String region, String departement, String numDepartement,
            String prefecture, String codeCommune, String latitude, String longitude) {
        this.pays = pays;
        this.codePostal = codePostal;
        this.nom = nom;
        this.region = region;
        this.departement = departement;
        this.numDepartement = numDepartement;
        this.prefecture = prefecture;
        this.codeCommune = codeCommune;
        this.latitude = latitude;
        this.longitude = longitude;
    }

    
    //GETTERS
    public String getPays() {
        return pays;
    }

    public String getCodePostal() {
        return codePostal;
    }

    public String getNom() {
        return nom;
    }

    public String getRegion() {
        return region;
    }

    public String getDepartement() {
        return departement;
    }

    public String getNumDeprtement() {
        return numDepartement;
    }

    public String getPrefecture() {
        return prefecture;
    }

    public String getCodeCommune() {
        return codeCommune;
    }

    public String getLatitude() {
        return latitude;
    }

    public String getLongitude() {
        return longitude;
    }
    
    
    //TOSTRING
    public String toString() {
        return String.format("%-40s%-10s\n", "Pays: " + pays, "Code Postal: " + codePostal) 
                + String.format("%-40s%-10s\n", "Nom: " + nom, "Region: " + region) 
                + String.format("%-40s%-10s\n", "Departement: " + departement, "Num Departement: " + numDepartement)
                + String.format("%-40s%-10s\n", "Prefecture: " + prefecture, "Code Commune: " + codeCommune)
                + String.format("%-40s%-10s\n\n", "Latitude: " + latitude, "Longitude: " + longitude);
    }
    }

}
package VilleEnumSet;

import java.util.EnumSet;
import java.util.Iterator;

import VilleEnumSet.VilleEnumSet.Ville;

public class MainVilleEnumSet {
    
public static void main(String[] args) {
        
        // Creating an EnumSet using allOf()
        EnumSet<Ville> villes = EnumSet.allOf(Ville.class);
        
        // Creating an iterator on games
        Iterator<Ville> iterate = villes.iterator();
        
        // Message
        System.out.println("EnumSet: " + "\n");
        
        while (iterate.hasNext()) {
            System.out.println(iterate.next());
        }
    }

}

my code returns this:

EnumSet: 

Pays: FR                                Code Postal: 75000
Nom: Paris                              Region: Ile de France
Departement: Paris                      Num Departement: 75
Prefecture: Paris                       Code Commune: 751
Latitude: 48.8534                       Longitude: 2.3488


Pays: FR                                Code Postal: 69000
Nom: Lyon                               Region: Auvergne-Rhone-Alpes
Departement: Rhone                      Num Departement: 69
Prefecture: Lyon                        Code Commune: 691
Latitude: 45.7485                       Longitude: 4.8467

For the CSV structure, each column corresponds to an attribute in the class "VilleEnumSet".

I have no idea what to do. I've looked for help, but many suggest to switch to list which I don't want to.

2

There are 2 best solutions below

2
Basil Bourque On BEST ANSWER

Enum not appropriate to your needs

An enum is meant to represent a small set of values known at compile-time. The names of these values are meant to be used within your source code, as discussed in the correct Answer by Bill K.

That does not match your situation.

You have a large number of values loaded at runtime. Representing such values is the purpose of a custom class. Grouping objects of your custom class is the purpose of the Java Collections Framework with lists, sets, queues, maps, and such.

record

If the main purpose of your custom class is to communicate data transparently and immutably, define your class as a record. The compiler implicitly creates the constructor, getters, equals & hashCode, and toString.

record Ville (String pays, String codePostal, String nom, String region, String departement, String numDepartement, String prefecture, String codeCommune, String latitude, String longitude) {}

By the way, know that as of Java 16 a record can be defined locally within a method, or nested within a class, or separately. (Ditto for enums and interfaces.)

As your CSV library imports each row of input, instantiate an object of your record class. Add to a collection object, likely an implementation of List. You may choose to instead use a NavigableSet/SortedSet after implementing the Comparable interface. In Java 21+, see SequencedSet.

If your inputs were to change during runtime, you can replace the collection of objects with fresh ones based on another data import. With enums, you can change the value of any member fields, but you cannot add or remove the named enum objects at runtime.

2
Bill K On

Although you absolutely would not use an enum for this, but there actually is a valid use case so It's worth an answer in case anyone else comes here.

The actual direct answer to your question is that you would want to generate the code for the enum progmatically. One way (the wrong way) is to just write a pre-compiler to build the file manually by emitting the beginning of the class as a constant string, the repeated lines for each group of data from the csv file and then the end of the class as another constant string. This pre-compiler is called by your buildfile before launching the actual java compiler.

This has problems--it will require a pre-compile step and will probably not integrate with your IDE.

The more correct way to do it would be to create an annotation processor. Annotations can be processed before your code is compiled and allows you to insert arbitrary code into the file.

If your annotation was inside the enum, your Annotation Processor could just emit the lines like this:

Paris("FR", "75000", "Paris", "Ile de France", "Paris", "75", "Paris", "751", "48.8534", "2.3488"),

and the rest of the file would be an actual java classfile contining an annotation that causes your Processor to be called.

The annotation processor is passed to the java compiler via a command line switch like "javac -processor (fullClassName)".

The nice thing about doing it this way is that you can tell the IDE that this is an annotation processor and it will ensure that adding a record to your CSV file makes the enum immideately available. It also prevents a temporary source file from being generated (Those almost always lead to trouble as people try to edit them by hand or something)