2

I'm making an Android app that has barcode scanning, and when I get a result back it comes back as an instance of the Barcode class. It has two values I'm interested in for the case of this question: barcode.format and barcode.valueFormat

Both of these values are integers, and as far as I can tell, these integers are matched up to what basically is (but isn't) an Enum as static fields in the Barcode class definition.

So for illustration purposes, you've got the class definition of Barcode like so:

public class Barcode extends AbstractSafeParcelable {
    ...
    public static final int ALL_FORMATS = 0;
    public static final int CODE_128 = 1;
    public static final int CODE_39 = 2;
    public static final int CODE_93 = 4;
    public static final int CODABAR = 8;
    public static final int DATA_MATRIX = 16;
    public static final int EAN_13 = 32;
    public static final int EAN_8 = 64;
    public static final int ITF = 128;
    public static final int QR_CODE = 256;
    public static final int UPC_A = 512;
    public static final int UPC_E = 1024;
    public static final int PDF417 = 2048;
    public static final int AZTEC = 4096;
    public static final int CONTACT_INFO = 1;
    public static final int EMAIL = 2;
    public static final int ISBN = 3;
    public static final int PHONE = 4;
    public static final int PRODUCT = 5;
    public static final int SMS = 6;
    public static final int TEXT = 7;
    public static final int URL = 8;
    public static final int WIFI = 9;
    public static final int GEO = 10;
    public static final int CALENDAR_EVENT = 11;
    public static final int DRIVER_LICENSE = 12;
    ...

And the barcode instance may come back with a barcode.format of 32 and a barcode.valueFormat of 5.

So on my end, what I would want is to take those numbers have have a string of 'EAN_13 : PRODUCT', but at a glance, these public static final fields don't make it easy to get the matching string values out.

I could of course make a Map<int, string> with all the known types shown here, but I don't like that idea because if any future formats get added, my Map will be out of date. And I don't like the idea of duplicating information that already exists as well.

So how do I convert these ints to their matching String values in a clean, future-proof way in Java?

TKoL
  • 13,158
  • 3
  • 39
  • 73
  • why don't you make them enum values? if you iterate over MyEnum.getValues() (or similar) the code doesn't change if you add new values – Stultuske Jun 11 '19 at 09:53
  • @Stultuske I did not make the `Barcode` class – TKoL Jun 11 '19 at 09:53
  • This class exists in Android, I'm just consuming it. – TKoL Jun 11 '19 at 09:54
  • ok. so, you'll need to get a list of the fields and iterate over them, and that way fill your map. This might be useful for that: https://stackoverflow.com/questions/16295949/get-all-fields-even-private-and-inherited-from-class – Stultuske Jun 11 '19 at 10:03
  • I've been looking at something similar. I'm close to a solution of just manually making my Map with the code here: https://stackoverflow.com/questions/271109/iterate-static-int-values-in-java – TKoL Jun 11 '19 at 10:07

2 Answers2

0

I made a small ans simple example, to show you how you can do it. Of course I recommend some filtering (for in case, make sure it is an int, before trying to parse), but that's a bit beyond the scope of this POC

public class Declared {

    public static final int ONE = 1; // you can add any number of field here
    public static final int TWO = 2; // it should still work. for private fields
                                     // check the api to verify

    public static void main(String[] args) throws Exception {
        // get the fields in the Declared class, currently ONE and TWO
        final Field[] declaredFields = Declared.class.getDeclaredFields();
        Map<String, Integer> fields = new HashMap<>();
        // add it field by field
        for ( Field field : declaredFields ) {
            fields.put(field.getName(), Integer.valueOf(field.getInt(null)));
        }
        // check the entryset for each element in your hashmap. it contains all you need
        final Set<Map.Entry<String, Integer>> entries = fields.entrySet();
        for ( Map.Entry<String, Integer> entry : entries ) {
            System.out.println(entry.getKey() + " - > " + entry.getValue());
        }
    }
}
Stultuske
  • 9,296
  • 1
  • 25
  • 37
  • This looks like it was implemented INSIDE the class that I'm trying to get the fields of. I cannot change that class, whatever solution has to happen outside of the class. – TKoL Jun 11 '19 at 10:30
  • @TKoL this was implemented inside that class because it's easier to post as a POC with limited code that way. It doesn't have to be in the same class. – Stultuske Jun 11 '19 at 10:43
0

I've realized it's technically impossible actually. There is nothing in the code to distinguish between the values that are supposed to be barcode.format and barcode.valueFormat, and the problem with that is that there are overlapping values. For example, Barcode.CONTACT_INFO and Barcode.CODE_128 both have the value of 1, and so even if I was able to use reflection to create my Map<Integer, String>, the map could only ever have one of those two values for 1.

I've already implemented how to get the map out:

public static Map<Integer, String> getIntFields(Class clz ) {
    Field[] fields = clz.getFields();
    HashMap<Integer, String> map = new HashMap<Integer, String>();

    for( Field field : fields ) {
        int modifiers = field.getModifiers();
        if (field.getType().equals(int.class) && Modifier.isStatic(field.getModifiers())) {
            String name = field.getName();
            int value = 0;
            try {
                value = field.getInt(null);
            } catch (IllegalAccessException e) {
                continue;
            }

            if (map.containsKey(value)) {
                // do nothing
            } else {
                map.put(value, name);
            }
        }
    }

    return map;
}

And I realized the problems above afterward. Kind of an annoying issue, don't know if it's because the Barcode class was poorly designed or it just wasn't meant to be used this way (probably the latter). But what I want is technically impossible, so I'll just use the int values rather than the strings.

TKoL
  • 13,158
  • 3
  • 39
  • 73