I maintain a large document archive and I often use bit fields to record the status of my documents during processing or when validating them. My legacy code simply uses static int constants such as:
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
This makes it pretty easy to indicate the state a document is in, by setting the appropriate flags. For example:
status = DOCUMENT_STATUS_NO_TIF_FILE | DOCUMENT_STATUS_NO_PDF_FILE;
Since the approach of using static constants is bad practice and because I would like to improve the code, I was looking to use Enums to achieve the same. There are a few requirements, one of them being the need to save the status into a database as a numeric type. So there is a need to transform the enumeration constants to a numeric value. Below is my first approach and I wonder if this is the correct way to go about this?
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); } }
You can't do it, since the language does not allow you. And for a good logical reason: subclassing an enum would only make sense if you could remove some enum values from the subclass, not add new ones. Otherwise you would break the Liskov Substitution Principle.
An enum type is a special data type that enables for a variable to be a set of predefined constants. The variable must be equal to one of the values that have been predefined for it. Common examples include compass directions (values of NORTH, SOUTH, EAST, and WEST) and the days of the week.
A standard enumeration provides a list of named integer values. These values can be accessed using either the name or the associated value. Using the name rather than the value from within your program makes the code much easier to read and maintain.
Because there is only one instance of each enum constant, it is permissible to use the == operator in place of the equals method when comparing two object references if it is known that at least one of them refers to an enum constant.
Instead of defining constructor parameters, you could simply use the internal ordinal()
value to calculate this.
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(); } }
Please note that now you should abstain from reordering, inserting (other than at the end) or deleting entries, otherwise the flag values will change, and the meaning of your database contents will change.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With