web-dev-qa-db-fra.com

Implémenter un champ binaire en utilisant Java énumérations

Je conserve une grande archive de documents et j'utilise souvent des champs de bits pour enregistrer l'état de mes documents pendant le traitement ou lors de leur validation. Mon code hérité utilise simplement des constantes statiques telles que:

static int DOCUMENT_STATUS_NO_STATE = 0
static int DOCUMENT_STATUS_OK = 1
static int DOCUMENT_STATUS_NO_TIF_FILE = 2
static int DOCUMENT_STATUS_NO_PDF_FILE = 4

Cela permet d'indiquer assez facilement l'état dans lequel se trouve un document en définissant les indicateurs appropriés. Par exemple:

status = DOCUMENT_STATUS_NO_TIF_FILE | DOCUMENT_STATUS_NO_PDF_FILE;

Étant donné que l'approche consistant à utiliser des constantes statiques est une mauvaise pratique et parce que j'aimerais améliorer le code, je cherchais à utiliser Enums pour y parvenir. Il existe quelques exigences, l'une d'entre elles étant la nécessité d'enregistrer l'état dans une base de données sous forme numérique. Il est donc nécessaire de transformer les constantes d'énumération en une valeur numérique. Voici ma première approche et je me demande si c'est la bonne façon de procéder?

class DocumentStatus{

    public enum StatusFlag {

        DOCUMENT_STATUS_NOT_DEFINED(1<<0),
        DOCUMENT_STATUS_OK(1<<1), 
        DOCUMENT_STATUS_MISSING_TID_DIR(1<<2),
        DOCUMENT_STATUS_MISSING_TIF_FILE(1<<3),
        DOCUMENT_STATUS_MISSING_PDF_FILE(1<<4),
        DOCUMENT_STATUS_MISSING_OCR_FILE(1<<5),
        DOCUMENT_STATUS_PAGE_COUNT_TIF(1<<6),
        DOCUMENT_STATUS_PAGE_COUNT_PDF(1<<7),
        DOCUMENT_STATUS_UNAVAILABLE(1<<8);


        private final long statusFlagValue;

        StatusFlag(long statusFlagValue) {
            this.statusFlagValue = statusFlagValue;
        }

        public long getStatusFlagValue(){
            return statusFlagValue;
        } 

       }


    /**
     * Translates a numeric status code into a Set of StatusFlag enums
     * @param numeric statusValue 
     * @return EnumSet representing a documents status
     */
    public EnumSet<StatusFlag> getStatusFlags(long statusValue) {
        EnumSet statusFlags = EnumSet.noneOf(StatusFlag.class);
        StatusFlag.each { statusFlag -> 
            long flagValue = statusFlag.statusFlagValue
            if ( (flagValue&statusValue ) == flagValue ) {
               statusFlags.add(statusFlag);
            }
        }
        return statusFlags;
    }


    /**
     * Translates a set of StatusFlag enums into a numeric status code
     * @param Set if statusFlags
     * @return numeric representation of the document status 
     */
    public long getStatusValue(Set<StatusFlag> flags) {
        long value=0;
        flags.each { statusFlag -> 
            value|=statusFlag.getStatusFlagValue() 
        }
        return value;
    }

     public static void main(String[] args) {

        DocumentStatus ds = new DocumentStatus();
        Set statusFlags = EnumSet.of(
            StatusFlag.DOCUMENT_STATUS_OK,
            StatusFlag.DOCUMENT_STATUS_UNAVAILABLE);

        assert ds.getStatusValue( statusFlags )==258 // 0000.0001|0000.0010

        long numericStatusCode = 56;
        statusFlags = ds.getStatusFlags(numericStatusCode);

        assert !statusFlags.contains(StatusFlag.DOCUMENT_STATUS_OK);
        assert statusFlags.contains(StatusFlag.DOCUMENT_STATUS_MISSING_TIF_FILE);
        assert statusFlags.contains(StatusFlag.DOCUMENT_STATUS_MISSING_PDF_FILE);
        assert statusFlags.contains(StatusFlag.DOCUMENT_STATUS_MISSING_OCR_FILE);

    }

}
41
soappatrol

votre approche est exactement la façon de le faire.

16
Ray Tayek

Au lieu de définir des paramètres de constructeur, vous pouvez simplement utiliser la valeur interne ordinal() pour le calculer.

public enum StatusFlag {

    DOCUMENT_STATUS_NOT_DEFINED,
    DOCUMENT_STATUS_OK, 
    DOCUMENT_STATUS_MISSING_TID_DIR,
    DOCUMENT_STATUS_MISSING_TIF_FILE,
    DOCUMENT_STATUS_MISSING_PDF_FILE,
    DOCUMENT_STATUS_MISSING_OCR_FILE,
    DOCUMENT_STATUS_PAGE_COUNT_TIF,
    DOCUMENT_STATUS_PAGE_COUNT_PDF,
    DOCUMENT_STATUS_UNAVAILABLE;


    public long getStatusFlagValue(){
        return 1 << this.ordinal();
    } 

}

Veuillez noter que vous devez maintenant vous abstenir de réorganiser, d'insérer (sauf à la fin) ou de supprimer des entrées, sinon les valeurs d'indicateur changeront et la signification du contenu de votre base de données changera.

33
Paŭlo Ebermann

Une façon légèrement meilleure serait de stocker le résultat de 1 << this.ordinal() dans un champ lorsque les valeurs d'énumération sont construites. De cette façon, vous n'avez pas à fournir chaque valeur manuellement et l'indicateur n'est calculé qu'une seule fois.

public enum StatusFlag {

  DOCUMENT_STATUS_NOT_DEFINED,
  DOCUMENT_STATUS_OK, 
  DOCUMENT_STATUS_MISSING_TID_DIR,
  DOCUMENT_STATUS_MISSING_TIF_FILE,
  DOCUMENT_STATUS_MISSING_PDF_FILE,
  DOCUMENT_STATUS_MISSING_OCR_FILE,
  DOCUMENT_STATUS_PAGE_COUNT_TIF,
  DOCUMENT_STATUS_PAGE_COUNT_PDF,
  DOCUMENT_STATUS_UNAVAILABLE;

  public final int flag;

  StatusFlag() { 
    this.flag = 1 << this.ordinal();
  } 
}

EDIT: il pourrait être utile d'avoir également la méthode isFlagSet.

8
ChrisBlom

Ne donnez pas de valeurs à vos énumérations. Utilisez un EnumSet pour les combiner, et utilisez Enum.ordinal() lorsque persistant afin de convertir vers/à partir d'un seul entier. Vous pouvez également trouver Class.getEnumConstants() utile lors de la reconstruction de l'ensemble à partir de l'entier.

5
OrangeDog

J'ai créé une bibliothèque complète pour ce problème: http://claude-martin.ch/enumbitset/

L'objectif principal était de stocker des ensembles de types d'énumération dans des champs de bits. Mais il prend également en charge d'autres types.

Avec cela, vous n'auriez pas besoin de méthodes supplémentaires comme votre "getStatusFlags ()". Il peut être utilisé sur n'importe quel type d'énumération existant simplement en ajoutant l'interface EnumBitSetHelper (il est utilisé comme un "trait"). Chaque constante enum peut ensuite créer un "EnumBitSet" qui a toutes les méthodes d'EnumSet et BitSet de Java. Ensuite, vous pouvez travailler avec ces ensembles de constantes d'énumération et les convertir en valeurs de champ de bits.

Il prend en charge de nombreux formats tels que BigInteger et long pour stocker facilement la valeur dans un champ de bits. Mais notez que cela ne fonctionne qu'avec Java version 8 et plus récente.

4
Claude Martin